home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / fscache / fscacheBlocks.c < prev    next >
C/C++ Source or Header  |  1992-12-18  |  106KB  |  3,626 lines

  1. /* 
  2.  * fscacheBlocks.c --
  3.  *
  4.  *    Routines to manage the <file-id, block #> cache.
  5.  *
  6.  * Copyright 1987 Regents of the University of California.
  7.  * All rights reserved.
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/fscache/fscacheBlocks.c,v 9.29 92/10/26 13:55:15 mgbaker Exp $ SPRITE (Berkeley)";
  19. #endif not lint
  20.  
  21. #include    <sprite.h>
  22. #include    <fs.h>
  23. #include    <fsutil.h>
  24. #include    <fscache.h>
  25. #include    <fscacheBlocks.h>
  26. #include    <fsStat.h>
  27. #include    <fsNameOps.h>
  28. #include    <fsdm.h>
  29. #include    <fsio.h>
  30. #include    <hash.h>
  31. #include    <vm.h>
  32. #include     <vmMach.h>
  33. #include    <proc.h>
  34. #include    <sys.h>
  35. #include    <rpc.h>
  36. #include    <recov.h>
  37.  
  38.  
  39. /*
  40.  * There are numerous points of synchronization in this module.  The CacheInfo
  41.  * struct in the handle, the block info struct and several global conditions
  42.  * and variables are used.
  43.  *
  44.  *     1) Synchronization between a block being fetched and a block having
  45.  *      good data in it.  The FSCACHE_IO_IN_PROGRESS flag is used for this.
  46.  *      If this flag is set in the call to Fscache_FetchBlock then the fetch
  47.  *      will block until the block becomes unreferenced.  Thus if the block
  48.  *      is already being used the user won't get their data stomped on.
  49.  *        Whenever Fscache_FetchBlock returns a block it sets the 
  50.  *      FSCACHE_IO_IN_PROGRESS flag in the block.  This flag will be cleared
  51.  *      whenever the block is released with Fscache_UnlockBlock or the
  52.  *      function Fscache_IODone is called.  While this flag is set in the
  53.  *      block all future fetches will block until the flag is cleared.
  54.  *
  55.  *     2) Synchronization for changing where a block lives on disk.  When a
  56.  *      block cleaner is writing out a block and Fscache_UnlockBlock is
  57.  *      called with a new location for the block, it blocks until the
  58.  *      block cleaner finishes.  The flag FSCACHE_BLOCK_BEING_WRITTEN in the
  59.  *      cache block struct indicates this.
  60.  *
  61.  *     3) Waiting for blocks from a file to be written back.  This is done by
  62.  *      waiting for the dirty list for the file to go empty.
  63.  *
  64.  *     4) Waiting for blocks from the whole cache to be written back.  We
  65.  *      keep track of the number of cache backends starts and wake the
  66.  *      waiting process when this number goes to zero.
  67.  *
  68.  *     5) Synchronizing informing the server when there are no longer any
  69.  *      dirty blocks in the cache for a file.  When the last dirty block
  70.  *      for a file is written the write is tagged saying that it is the
  71.  *      last dirty block.  However the server is also told when a file is
  72.  *      closed if no dirty blocks are left.  Since all writes out of the
  73.  *      cache are unsynchronized there is a race between the close and the
  74.  *      delayed write back.  This is solved by using the following 
  75.  *      synchronization.  When a file is closed the function 
  76.  *      Fscache_PreventWriteBacks is called.  This function will not return
  77.  *      until there are no block cleaners active on the file.  When it
  78.  *      returns it sets the FSCACHE_CLOSE_IN_PROGRESS flag in the cacheInfo
  79.  *      struct in the file handle and it returns the number of dirty blocks.
  80.  *      All subsequent block writes are blocked until the function
  81.  *      Fscache_AllowWriteBacks is called.  Thus the number of dirty blocks
  82.  *      in the cache for the file is accurate on close because no dirty
  83.  *      blocks can be written out while the file is being closed.
  84.  *      Likewise when a block cleaner writes out the last dirty block for
  85.  *      a file and it tells the server on the write that its the last
  86.  *      dirty block the server knows that it can believe the block cleaner
  87.  *      if the file is closed.  This is because if its closed then it must
  88.  *      have been closed when the  block cleaner did the write
  89.  *      (all closes are prohibited during the write) and thus there
  90.  *      is no way that more dirty blocks can be put into the cache.
  91.  *      If its open then the server ignores what the block cleaner
  92.  *      says because it will get told again when the file is closed.
  93.  */
  94.  
  95. /*
  96.  * Monitor lock.
  97.  */
  98. static Sync_Lock    cacheLock = Sync_LockInitStatic("Fs:blockCacheLock");
  99. #define    LOCKPTR    &cacheLock
  100.  
  101. /*
  102.  * Condition variables.
  103.  */
  104. Sync_Condition    cleanBlockCondition;    /* Condition that block 
  105.                          * allocator waits on when all 
  106.                          * blocks are dirty. */
  107. Sync_Condition    writeBackComplete;     /* Condition to wait on when 
  108.                          * are waiting for the write 
  109.                          * back of blocks in the cache 
  110.                          * to complete. */
  111. Sync_Condition    closeCondition;        /* Condition to wait on when
  112.                          * are waiting for the block
  113.                          * cleaner to finish to write
  114.                          * out blocks for this file. */
  115. static unsigned int filewriteBackTime;        /* Write back all blocks in
  116.                          * the cache file 
  117.                          * descriptors that were
  118.                          * dirtied before this 
  119.                          * time. */
  120. static int    numBackendsActive;        /* Number of backend write back
  121.                              * processes currently active.
  122.                                  */
  123. /*
  124.  * Pointer to LRU list that is used for block allocation.
  125.  */
  126. static    List_Links    lruListHdr;
  127. #define    lruList        (&lruListHdr)
  128.  
  129. /*
  130.  * There are two free lists.  The first contains blocks that are in pages that
  131.  * only contain free blocks.  The second contains blocks that are in pages that
  132.  * contain non-free blocks.  When the physical pages size <= the block size then
  133.  * the second list will always be empty.
  134.  */
  135. static    List_Links    totFreeListHdr;
  136. #define    totFreeList    (&totFreeListHdr)
  137. static    List_Links    partFreeListHdr;
  138. #define    partFreeList    (&partFreeListHdr)
  139.  
  140. /*
  141.  * Pointer to list of unmapped blocks.
  142.  */
  143. static    List_Links    unmappedListHdr;
  144. #define    unmappedList    (&unmappedListHdr)
  145.  
  146. /*
  147.  * List of Fscache_Backend's that could have file in the cache.
  148.  * This list is kept so CacheWriteBack has a list of all the
  149.  * backends that could have files in the cache. 
  150.  */
  151. static    List_Links    backendListHdr;
  152. #define    backendList     (&backendListHdr)
  153.  
  154. /*
  155.  * Writes and reads can block on a full cache.  This list records the
  156.  * processes that are blocked on this condition.
  157.  */
  158. List_Links fscacheFullWaitListHdr;
  159. List_Links *fscacheFullWaitList = &fscacheFullWaitListHdr;
  160.  
  161. /*
  162.  * Hash tables for blocks.
  163.  */
  164. static    Hash_Table    blockHashTableStruct;
  165. static    Hash_Table    *blockHashTable = &blockHashTableStruct;
  166.  
  167. /*
  168.  * The key to use for the block hash and a macro to set it.  The fact
  169.  * that the key includes a pointer into the I/O handle for the block
  170.  * means that this handle has to be kept around until there are no
  171.  * blocks left in the cache.
  172.  */
  173. typedef    struct {
  174.     Fscache_FileInfo *cacheInfoPtr;
  175.     int        blockNumber;
  176. } BlockHashKey;
  177. #define    SET_BLOCK_HASH_KEY(blockHashKey, ZcacheInfoPtr, fileBlock) \
  178.     (blockHashKey).cacheInfoPtr = ZcacheInfoPtr; \
  179.     (blockHashKey).blockNumber = fileBlock;
  180.  
  181. /*
  182.  * Miscellaneous variables.
  183.  */
  184. static    Address    blockCacheStart;    /* The address of the beginning of the
  185.                           block cache. */
  186. static    Address    blockCacheEnd;        /* The maximum virtual address for the 
  187.                      * cache.*/
  188. static    int    pageSize;        /* The size of a physical page. */
  189. static    int    blocksPerPage;        /* Number of blocks in a page. */
  190.  
  191. static  int    numAvailBlocks;        /* Number of cache block available for 
  192.                      * use without waiting. */
  193. static  int    minNumAvailBlocks;    /* Minimum number of cache blocks to
  194.                      * keep available. */
  195.  
  196. /*
  197.  * Macros for large page sizes.
  198.  *
  199.  *    GET_OTHER_BLOCK        Return a pointer to the other block in
  200.  *                the page given a pointer to one of the blocks.
  201.  *    PAGE_IS_8K        Return true if the VM page size is 8K.
  202.  */
  203. #define GET_OTHER_BLOCK(blockPtr) \
  204.     (((int) blockPtr->blockAddr & (pageSize - 1)) != 0) ?  \
  205.         blockPtr - 1 : blockPtr + 1
  206.  
  207. #define    PAGE_IS_8K    (pageSize == 8192)
  208.  
  209. /*
  210.  * External symbols.
  211.  */
  212.  
  213. int    fscache_MaxBlockCleaners = FSCACHE_MAX_CLEANER_PROCS;
  214.  
  215. /*
  216.  * Internal functions.
  217.  */
  218. static void CacheFileInvalidate _ARGS_((Fscache_FileInfo *cacheInfoPtr, 
  219.             int firstBlock, int lastBlock));
  220. static void DeleteBlockFromDirtyList _ARGS_((Fscache_Block *blockPtr));
  221. static void StartFileSync _ARGS_((Fscache_FileInfo *cacheInfoPtr));
  222. static void CacheWriteBack _ARGS_((unsigned int writeBackTime, 
  223.             int *blocksSkippedPtr, Boolean writeTmpFiles));
  224. static Boolean CreateBlock _ARGS_((Boolean retBlock,
  225.             Fscache_Block **blockPtrPtr));
  226. static Boolean DestroyBlock _ARGS_((Boolean retOnePage, int *pageNumPtr));
  227. static Fscache_Block *FetchBlock _ARGS_((Boolean canWait, Boolean cantBlock));
  228.         /*
  229.          * Second parameter below is for ASPLOS measurements and can be
  230.          * removed after all that's over.  Mary 2/14/92
  231.          */
  232. static void StartBackendWriteback _ARGS_((Fscache_Backend *backendPtr, Boolean fileFsynced));
  233. static void PutOnFreeList _ARGS_((Fscache_Block *blockPtr));
  234. static void PutFileOnDirtyList _ARGS_((Fscache_FileInfo *cacheInfoPtr,
  235.             time_t oldestDirtyBlockTime));
  236. static void PutBlockOnDirtyList _ARGS_((Fscache_Block *blockPtr, 
  237.             Boolean onFront));
  238. static Hash_Entry *GetUnlockedBlock _ARGS_((BlockHashKey *blockHashKeyPtr, 
  239.             int blockNum));
  240. static void DeleteBlock _ARGS_((Fscache_Block *blockPtr));
  241.  
  242.  
  243. /*
  244.  * ----------------------------------------------------------------------------
  245.  *
  246.  * Fscache_Init --
  247.  *
  248.  *     Initialize the cache.
  249.  *
  250.  * Results:
  251.  *    None.
  252.  *
  253.  * Side effects:
  254.  *    Hash tables initialized and memory allocated for the cache.
  255.  *
  256.  * ----------------------------------------------------------------------------
  257.  */
  258. void
  259. Fscache_Init(blockHashSize)
  260.     int    blockHashSize;    /* The number of hash table entries to put in the
  261.                block hash table for starters. */
  262. {
  263.     register    Address        blockAddr;
  264.     Address            listStart;
  265.     register    Fscache_Block    *blockPtr;
  266.     register    int        i;
  267.  
  268.     Vm_FsCacheSize(&blockCacheStart, &blockCacheEnd);
  269.     pageSize = Vm_GetPageSize();
  270.     blocksPerPage = pageSize / FS_BLOCK_SIZE;
  271.     /*
  272.      * Currently the cache code only handles the case for 
  273.      * (pageSize != FS_BLOCK_SIZE) when the block size is 4K and 
  274.      * the page size 8K.
  275.      */
  276.     if (!((pageSize == FS_BLOCK_SIZE) ||
  277.       ((FS_BLOCK_SIZE == 4096) && PAGE_IS_8K))) {
  278.     panic("Bad pagesize (%d) for file cache code\n", pageSize);
  279.     }
  280.     fs_Stats.blockCache.maxNumBlocks = 
  281.             (blockCacheEnd - blockCacheStart + 1) / FS_BLOCK_SIZE;
  282.  
  283.     fs_Stats.blockCache.minCacheBlocks = FSCACHE_MIN_BLOCKS;
  284.     fs_Stats.blockCache.maxCacheBlocks = fs_Stats.blockCache.maxNumBlocks;
  285.  
  286.     /*
  287.      * Allocate space for the cache block list.
  288.      */
  289.     listStart = Vm_RawAlloc((int)fs_Stats.blockCache.maxNumBlocks *
  290.                 sizeof(Fscache_Block));
  291.     blockPtr = (Fscache_Block *) listStart;
  292.  
  293.     /*
  294.      * Initialize the hash table.
  295.      */
  296.     Hash_Init(blockHashTable, blockHashSize, Hash_Size(sizeof(BlockHashKey)));
  297.  
  298.     /*
  299.      * Initialize all lists.
  300.      */
  301.     List_Init(lruList);
  302.     List_Init(totFreeList);
  303.     List_Init(partFreeList);
  304.     List_Init(backendList);
  305.     List_Init(unmappedList);
  306.     List_Init(fscacheFullWaitList);
  307.  
  308.     for (i = 0, blockAddr = blockCacheStart, 
  309.         blockPtr = (Fscache_Block *)listStart;
  310.      i < fs_Stats.blockCache.maxNumBlocks; 
  311.      i++, blockPtr++, blockAddr += FS_BLOCK_SIZE) {
  312.     blockPtr->flags = FSCACHE_NOT_MAPPED;
  313.     blockPtr->blockAddr = blockAddr;
  314.     blockPtr->refCount = 0;
  315.     List_Insert(&blockPtr->useLinks, LIST_ATREAR(unmappedList));
  316.     }
  317. #ifdef sun4
  318.     {
  319.     /*
  320.      * Hack that allows VM to be backward compat with old block cache
  321.      * code.  This should have been removed by the time you see it.
  322.      */
  323.     extern Boolean vmMachCanStealFileCachePmegs;
  324.     vmMachCanStealFileCachePmegs = TRUE;
  325.     }
  326. #endif
  327.     /*
  328.      * Give enough blocks memory so that the minimum cache size requirement
  329.      * is met.
  330.      */
  331.     fs_Stats.blockCache.numCacheBlocks = 0;
  332.     while (fs_Stats.blockCache.numCacheBlocks < 
  333.                     fs_Stats.blockCache.minCacheBlocks) {
  334.     if (!CreateBlock(FALSE, (Fscache_Block **) NIL)) {
  335.         printf("Fscacahe_Init: Couldn't create block\n");
  336.         fs_Stats.blockCache.minCacheBlocks = 
  337.                     fs_Stats.blockCache.numCacheBlocks;
  338.     }
  339.     }
  340.     minNumAvailBlocks = fs_Stats.blockCache.minCacheBlocks/2;
  341.     printf("FS Cache has %d %d-Kbyte blocks (%d max)\n",
  342.         fs_Stats.blockCache.minCacheBlocks, FS_BLOCK_SIZE / 1024,
  343.         fs_Stats.blockCache.maxNumBlocks);
  344.  
  345. }
  346.  
  347.  
  348. /*
  349.  * ----------------------------------------------------------------------------
  350.  *
  351.  * Fscache_FileInfoInit --
  352.  *
  353.  *     Initialize the cache information for a file.  Called when setting
  354.  *    up a handle for a file that uses the block cache.
  355.  *
  356.  * Results:
  357.  *    None.
  358.  *
  359.  * Side effects:
  360.  *    Set up the fields of the Fscache_FileInfo struct. First file may
  361.  *    cause the backend to be initialized.
  362.  *    
  363.  * ----------------------------------------------------------------------------
  364.  */
  365. void
  366. Fscache_FileInfoInit(cacheInfoPtr, hdrPtr, version, cacheable, attrPtr, 
  367.         backendPtr)
  368.     register Fscache_FileInfo *cacheInfoPtr;    /* Information to initialize. */
  369.     Fs_HandleHeader         *hdrPtr;        /* Back pointer to handle */
  370.     int                 version;        /* Used to check consistency */
  371.     Boolean             cacheable;        /* TRUE if server says we can
  372.                          * cache */
  373.     Fscache_Attributes         *attrPtr;        /* File attributes */
  374.     Fscache_Backend         *backendPtr;    /* Cache backend for
  375.                          * this file. */
  376. {
  377.     List_InitElement(&cacheInfoPtr->links);
  378.     List_Init(&cacheInfoPtr->dirtyList);
  379.     List_Init(&cacheInfoPtr->blockList);
  380.     List_Init(&cacheInfoPtr->indList);
  381.     cacheInfoPtr->flags = (cacheable ? 0 : FSCACHE_FILE_NOT_CACHEABLE);
  382.     cacheInfoPtr->version = version;
  383.     cacheInfoPtr->hdrPtr = hdrPtr;
  384.     cacheInfoPtr->blocksWritten = 0;
  385.     cacheInfoPtr->noDirtyBlocks.waiting = 0;
  386.     cacheInfoPtr->blocksInCache = 0;
  387.     cacheInfoPtr->numDirtyBlocks = 0;
  388.     cacheInfoPtr->lastTimeTried = 0;
  389.     cacheInfoPtr->oldestDirtyBlockTime = Fsutil_TimeInSeconds();
  390.     cacheInfoPtr->attr = *attrPtr;
  391.     cacheInfoPtr->backendPtr = backendPtr;
  392.     Sync_LockInitDynamic(&cacheInfoPtr->lock, "Fs:perFileCacheLock");
  393. }
  394.  
  395. /*
  396.  * ----------------------------------------------------------------------------
  397.  *
  398.  * Fscache_InfoSyncLockCleanup --
  399.  *
  400.  *     Clean up Sync_Lock tracing for the cache lock.
  401.  *
  402.  * Results:
  403.  *    None.
  404.  *
  405.  * Side effects:
  406.  *    Set up the fields of the Fscache_FileInfo struct.
  407.  *
  408.  * ----------------------------------------------------------------------------
  409.  */
  410. /*ARGSUSED*/
  411. void
  412. Fscache_InfoSyncLockCleanup(cacheInfoPtr)
  413.     Fscache_FileInfo *cacheInfoPtr;
  414. {
  415.     Sync_LockClear(&cacheInfoPtr->lock);
  416. }
  417.  
  418.  
  419.  
  420. /*
  421.  *----------------------------------------------------------------------
  422.  *
  423.  * Fscache_RegisterBackend --
  424.  *
  425.  *    Allocate and initialize the Fscache_Backend data structure for a 
  426.  *    cache backend.
  427.  *
  428.  * Results:
  429.  *    The malloc'ed cache backend.
  430.  *
  431.  * Side effects:
  432.  *    The backend is added to the backendList.
  433.  *
  434.  *----------------------------------------------------------------------
  435.  */
  436.  
  437. Fscache_Backend *
  438. Fscache_RegisterBackend(ioProcsPtr, clientData, flags)
  439.     Fscache_BackendRoutines   *ioProcsPtr;
  440.     ClientData    clientData;
  441.     int         flags;    /* Backend flags. */
  442. {
  443.     Fscache_Backend    *backendPtr;
  444.  
  445.     LOCK_MONITOR;
  446.  
  447.     backendPtr = (Fscache_Backend *) malloc(sizeof(Fscache_Backend));
  448.     bzero((char *) backendPtr, sizeof(Fscache_Backend));
  449.  
  450.     List_Init((List_Links *)backendPtr);
  451.     backendPtr->clientData = clientData;
  452.     backendPtr->flags = flags;
  453.     List_Init((List_Links *)&(backendPtr->dirtyListHdr));
  454.     backendPtr->ioProcs = *ioProcsPtr;
  455.  
  456.     List_Insert((List_Links *)backendPtr, LIST_ATREAR(backendList));
  457.     UNLOCK_MONITOR;
  458.     return backendPtr;
  459. }
  460.  
  461. /*
  462.  *----------------------------------------------------------------------
  463.  *
  464.  * Fscache_UnregisterBackend --
  465.  *
  466.  *    Deallocate a Fscache_Backend data structure allocated with
  467.  *    Fscache_RegisterBackend.
  468.  *
  469.  * Results:
  470.  *
  471.  * Side effects:
  472.  *    Backend is removed from the list of active backends and its memory
  473.  *    is freed.
  474.  *
  475.  *----------------------------------------------------------------------
  476.  */
  477.  
  478. void
  479. Fscache_UnregisterBackend(backendPtr)
  480.     Fscache_Backend    *backendPtr; /* Backend to deallocate. */
  481. {
  482.  
  483.     LOCK_MONITOR;
  484.  
  485.     if (!List_IsEmpty(&(backendPtr->dirtyListHdr))) {
  486.     UNLOCK_MONITOR;
  487.     panic("Fscache_UnregisterBackend: Backend has dirty files.\n");
  488.     return;
  489.     }
  490.     List_Remove((List_Links *)backendPtr);
  491.     /*
  492.      * Until we get a better grip on the file handle code we can't free
  493.      * the backendPtr because the cacheInfoPtr in the handle may still
  494.      * point at it.  
  495.      *
  496.      * THIS IS A MEMORY LEAK - 48 bytes per backend unregistered. 
  497.      * Currently this only happens when an LFS file system is 
  498.      * unattached.
  499.      */
  500.  
  501. #ifdef notdef
  502. #ifndef CLEAN
  503.     /*
  504.      * NIL out these fields in hope in catching any bad code that
  505.      * trys to use them.
  506.      */
  507.     backendPtr->clientData = (ClientData) NIL;
  508.     backendPtr->ioProcs.allocate = (ReturnStatus (*)()) NIL;
  509.     backendPtr->ioProcs.blockRead = (ReturnStatus (*)()) NIL;
  510.     backendPtr->ioProcs.blockWrite = (ReturnStatus (*)()) NIL;
  511.     backendPtr->ioProcs.reallocBlock = (void (*)()) NIL;
  512.     backendPtr->ioProcs.startWriteBack = (ReturnStatus (*)()) NIL;
  513. #endif /* not CLEAN */
  514.  
  515.     free((char *) backendPtr);
  516. #endif /* notdef */
  517.  
  518.     UNLOCK_MONITOR;
  519.     return;
  520. }
  521.  
  522.  
  523.  
  524. /*
  525.  * ----------------------------------------------------------------------------
  526.  *
  527.  *    Functions for external block access.  Includes functions to fetch,
  528.  *    release and truncate cache blocks.
  529.  *
  530.  * ----------------------------------------------------------------------------
  531.  */
  532.  
  533. /*
  534.  * ----------------------------------------------------------------------------
  535.  *
  536.  * Fscache_FetchBlock --
  537.  *
  538.  *    Return in *blockPtrPtr a pointer to a block in the 
  539.  *    cache that corresponds to virtual block blockNum in the file 
  540.  *    identified by *cacheInfoPtr.  If the block for the file is not in the 
  541.  *    cache then *foundPtr is set to FALSE and if allocate is 
  542.  *    TRUE, then a clean block is returned.  Otherwise a pointer to the 
  543.  *    actual data block is returned and *foundPtr is set to TRUE.
  544.  *    The block that is returned is locked down in the cache (i.e. it cannot
  545.  *    be replaced) until it is unlocked by Fscache_UnlockBlock.  If the block
  546.  *    isn't found or the FSCACHE_IO_IN_PROGRESS flag is given then the block
  547.  *    is marked as IO in progress and must be either unlocked by 
  548.  *    Fscache_UnlockBlock or marked as IO done by Fscache_IODone.
  549.  *
  550.  * Results:
  551.  *    None.
  552.  *
  553.  * Side effects:
  554.  *    New block may be allocated out of the block cache, block that 
  555.  *    is returned is locked, and the block may be set as IO in progress.
  556.  *
  557.  * ----------------------------------------------------------------------------
  558.  *
  559.  */
  560. static Fscache_Block *lostBlockPtr;
  561.  
  562. ENTRY void
  563. Fscache_FetchBlock(cacheInfoPtr, blockNum, flags, blockPtrPtr, foundPtr)
  564.     register Fscache_FileInfo *cacheInfoPtr; /* Pointer to the cache state 
  565.                    * for the file. */
  566.     int         blockNum;    /* Virtual block number in the file. */
  567.     int         flags;        /* FSCACHE_DONT_BLOCK |
  568.                  * FSCACHE_CANT_BLOCK |
  569.                  * FSCACHE_READ_AHEAD_BLOCK |
  570.                  * FSCACHE_IO_IN_PROGRESS
  571.                  * plus the type of block */
  572.     Fscache_Block **blockPtrPtr; /* Where pointer to cache block information
  573.                  * structure is returned. The structure
  574.                  * contains the virtual address of the 
  575.                  * actual cache block. */
  576.     Boolean    *foundPtr;    /* TRUE if the block is present in the
  577.                  * cache, FALSE if not. */
  578. {
  579.     BlockHashKey        blockHashKey;
  580.     register    Hash_Entry    *hashEntryPtr;
  581.     register    Fscache_Block    *blockPtr;
  582.     Fscache_Block        *otherBlockPtr;
  583.     Fscache_Block        *newBlockPtr;
  584.     time_t            refTime;
  585.     Boolean        cantBlock = (flags & FSCACHE_CANT_BLOCK);
  586.     Boolean        dontBlock = (flags & FSCACHE_DONT_BLOCK);
  587.  
  588.     LOCK_MONITOR;
  589.  
  590.     *blockPtrPtr = (Fscache_Block *)NIL;
  591.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, blockNum);
  592.  
  593.     do {
  594.     /*
  595.      * Keep re-hashing until we get a block.  If we ever have to
  596.      * wait in this loop then the hash table can change out from
  597.      * under us, so we always rehash.
  598.      */
  599.     hashEntryPtr = Hash_Find(blockHashTable, (Address) &blockHashKey);
  600.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  601.     if (blockPtr != (Fscache_Block *) NIL) {
  602.         Boolean    blockBusy;
  603.         *foundPtr = TRUE;
  604.         if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  605.         UNLOCK_MONITOR;
  606.         panic("Fscache_FetchBlock hashing error\n");
  607.         *foundPtr = FALSE;
  608.         return;
  609.         }
  610.         blockBusy = ((blockPtr->refCount > 0) || 
  611.             (blockPtr->flags & (FSCACHE_IO_IN_PROGRESS|
  612.                     FSCACHE_BLOCK_BEING_WRITTEN)));
  613.         if ( ((flags & FSCACHE_IO_IN_PROGRESS) && blockBusy) ||
  614.           (blockPtr->flags & FSCACHE_IO_IN_PROGRESS)) {
  615.         if (!dontBlock) {
  616.             /*
  617.              * Wait until it becomes unlocked, or return
  618.              * found = TRUE and block = NIL if caller can't block.
  619.              */
  620.             (void)Sync_Wait(&blockPtr->ioDone, FALSE);
  621.         }
  622.         blockPtr = (Fscache_Block *)NIL;
  623.         } else {
  624.         blockPtr->refCount++;
  625.         if (blockPtr->refCount == 1) {
  626.             VmMach_LockCachePage(blockPtr->blockAddr);
  627.             if (!(blockPtr->flags & FSCACHE_BLOCK_DIRTY)) {
  628.             numAvailBlocks--;
  629.             if (numAvailBlocks < 0) {
  630.                 panic("Fscache_FetchBlock numAvailBlocks < 0\n");
  631.             }
  632.             }
  633.         }
  634.         if (flags & FSCACHE_IO_IN_PROGRESS) {
  635.             blockPtr->flags |= FSCACHE_IO_IN_PROGRESS;
  636.         }
  637.         }
  638.     } else {
  639.         /*
  640.          * Have to allocate a block.  If there is a free block use it, or
  641.          * take a block off of the lru list, or make a new one.
  642.          */
  643.         *foundPtr = FALSE;
  644.         if ((numAvailBlocks > minNumAvailBlocks) || cantBlock) {
  645.         /*
  646.          * If we have enought blocks available take a free one.
  647.          */
  648.         if (!List_IsEmpty(partFreeList)) {
  649.             /*
  650.              * Use partially free blocks first.
  651.              */
  652.             fs_Stats.blockCache.numFreeBlocks--;
  653.             fs_Stats.blockCache.partFree++;
  654.             blockPtr = USE_LINKS_TO_BLOCK(List_First(partFreeList));
  655.             List_Remove(&blockPtr->useLinks);
  656.         } else if (!List_IsEmpty(totFreeList)) {
  657.             /*
  658.              * Can't find a partially free block so use a totally free
  659.              * block.
  660.              */
  661.             fs_Stats.blockCache.numFreeBlocks--;
  662.             fs_Stats.blockCache.totFree++;
  663.             blockPtr = USE_LINKS_TO_BLOCK(List_First(totFreeList));
  664.             List_Remove(&blockPtr->useLinks);
  665.             if (PAGE_IS_8K) {
  666.             otherBlockPtr = GET_OTHER_BLOCK(blockPtr);
  667.             List_Move(&otherBlockPtr->useLinks,
  668.                       LIST_ATREAR(partFreeList));
  669.             }
  670.         }
  671.         }
  672.         if (blockPtr == (Fscache_Block *) NIL) {
  673.         /*
  674.          * Can't find any free blocks so have to use one of our blocks
  675.          * or create new ones.
  676.          */
  677.         if (fs_Stats.blockCache.numCacheBlocks >= 
  678.                     fs_Stats.blockCache.maxCacheBlocks) {
  679.             /*
  680.              * We can't have anymore blocks so reuse one of our own.
  681.              */
  682.             blockPtr = FetchBlock(!dontBlock, cantBlock);
  683.             if ((blockPtr == (Fscache_Block *) NIL) && cantBlock) {
  684.             goto getBlock;
  685.             }
  686.         } else {
  687.             /*
  688.              * Grow the cache if VM has an older page than we have.
  689.              */
  690.             refTime = Vm_GetRefTime();
  691.             blockPtr = USE_LINKS_TO_BLOCK(List_First(lruList));
  692.             if (blockPtr->timeReferenced > refTime) {
  693.         getBlock:
  694.             if (!CreateBlock(TRUE, &newBlockPtr)) {
  695.                 blockPtr = FetchBlock(!dontBlock, cantBlock);
  696.             } else {
  697.                 fs_Stats.blockCache.unmapped++;
  698.                 blockPtr = newBlockPtr;
  699.             }
  700.             } else {
  701.             /*
  702.              * We have an older block than VM's oldest page so reuse
  703.              * the block.
  704.              */
  705.             blockPtr = FetchBlock(!dontBlock, cantBlock);
  706.             }
  707.         }
  708.         }
  709.         /*
  710.          * If blockPtr is NIL we waited for room in the cache or
  711.          * for a busy cache block.  Now we'll retry all the various
  712.          * ploys to get a free block.
  713.          */
  714.     }
  715.     } while ((blockPtr == (Fscache_Block *)NIL) && !dontBlock);
  716.  
  717.     if ((*foundPtr == FALSE) && (blockPtr != (Fscache_Block *)NIL)) {
  718.     cacheInfoPtr->blocksInCache++;
  719.     blockPtr->cacheInfoPtr = cacheInfoPtr;
  720.     blockPtr->refCount = 1;
  721.     VmMach_LockCachePage(blockPtr->blockAddr);
  722.     blockPtr->flags = flags & (FSCACHE_DATA_BLOCK | FSCACHE_IND_BLOCK |
  723.                    FSCACHE_DESC_BLOCK | FSCACHE_DIR_BLOCK |
  724.                    FSCACHE_READ_AHEAD_BLOCK);
  725.     blockPtr->flags |= FSCACHE_IO_IN_PROGRESS;
  726.     blockPtr->fileNum = cacheInfoPtr->hdrPtr->fileID.minor;
  727.     blockPtr->blockNum = blockNum;
  728.     blockPtr->blockSize = -1;
  729.     blockPtr->timeDirtied = 0;
  730.     blockPtr->timeReferenced = Fsutil_TimeInSeconds();
  731.     *blockPtrPtr = blockPtr;
  732.     if (Hash_GetValue(hashEntryPtr) != (char *)NIL) {
  733.         lostBlockPtr = (Fscache_Block *)Hash_GetValue(hashEntryPtr);
  734.         UNLOCK_MONITOR;
  735.         panic("Fscache_FetchBlock: hashEntryPtr->value changed\n");
  736.         LOCK_MONITOR;
  737.     }
  738.     Hash_SetValue(hashEntryPtr, blockPtr);
  739.     List_Insert(&(blockPtr->useLinks), LIST_ATREAR(lruList));
  740.     List_InitElement(&blockPtr->fileLinks);
  741.     if (flags & FSCACHE_IND_BLOCK) {
  742.         List_Insert(&blockPtr->fileLinks, LIST_ATREAR(&cacheInfoPtr->indList));
  743.     } else {
  744.         List_Insert(&blockPtr->fileLinks,LIST_ATREAR(&cacheInfoPtr->blockList));
  745.     }
  746.     numAvailBlocks--;
  747.     if (numAvailBlocks < 0) {
  748.         panic("Fscache_FetchBlock numAvailBlocks < 0.\n");
  749.     }
  750.     }
  751.     *blockPtrPtr = blockPtr;
  752.     UNLOCK_MONITOR;
  753.     return;
  754. }
  755.  
  756.  
  757. /*
  758.  * ----------------------------------------------------------------------------
  759.  *
  760.  * Fscache_IODone --
  761.  *
  762.  *    Remove the IO-in-progress flag from the cache block flags field.
  763.  *
  764.  * Results:
  765.  *    None.
  766.  *
  767.  * Side effects:
  768.  *    The IO-in-progress flag is removed from the cache block flags field.
  769.  *
  770.  * ----------------------------------------------------------------------------
  771.  *
  772.  */
  773. ENTRY void
  774. Fscache_IODone(blockPtr)
  775.     Fscache_Block *blockPtr;    /* Pointer to block information for block.*/
  776. {
  777.     LOCK_MONITOR;
  778.  
  779.     Sync_Broadcast(&blockPtr->ioDone);
  780.     blockPtr->flags &= ~FSCACHE_IO_IN_PROGRESS;
  781.  
  782.     UNLOCK_MONITOR;
  783. }
  784.  
  785. /*
  786.  * ----------------------------------------------------------------------------
  787.  *
  788.  * Fscache_UnlockBlock --
  789.  *
  790.  *    Release the lock on the cache block pointed to by blockPtr.
  791.  *
  792.  * Results:
  793.  *    None.
  794.  *
  795.  * Side effects:
  796.  *    The lock count of the block is decremented.
  797.  *
  798.  * ----------------------------------------------------------------------------
  799.  *
  800.  */
  801. ENTRY void
  802. Fscache_UnlockBlock(blockPtr, timeDirtied, diskBlock, blockSize, flags)
  803.     Fscache_Block *blockPtr;    /* Pointer to block information for block
  804.                    that is to be released. */
  805.     time_t     timeDirtied;    /* Time in seconds that the block was 
  806.                    dirtied. */
  807.     int         diskBlock;    /* If not -1 is the block on disk where this
  808.                    block resides.  For remote blocks this 
  809.                    should be the same as blockPtr->blockNum.*/
  810.     int         blockSize;    /* The number of valid bytes in this block. */
  811.     int         flags;        /* FSCACHE_DELETE_BLOCK | FSCACHE_CLEAR_READ_AHEAD |
  812.                  * FSCACHE_BLOCK_UNNEEDED | FSCACHE_DONT_WRITE_THRU
  813.                  * FSCACHE_WRITE_TO_DISK | FSCACHE_BLOCK_BEING_CLEANED*/
  814. {
  815.     LOCK_MONITOR;
  816.  
  817.     if (blockPtr->flags & FSCACHE_BLOCK_FREE) {
  818.     panic("Checking in free block\n");
  819.     }
  820.  
  821.     if (blockPtr->flags & FSCACHE_IO_IN_PROGRESS) {
  822.     Sync_Broadcast(&blockPtr->ioDone);
  823.     blockPtr->flags &= ~FSCACHE_IO_IN_PROGRESS;
  824.     }
  825.  
  826.     if (flags & FSCACHE_DELETE_BLOCK) {
  827.     /*
  828.      * The caller is deleting this block from the cache.  Decrement the
  829.      * lock count and then invalidate the block.
  830.      */
  831.     blockPtr->refCount--;
  832.     if (blockPtr->refCount == 0) { 
  833.         VmMach_UnlockCachePage(blockPtr->blockAddr);
  834.         if (!(blockPtr->flags & 
  835.             (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN))) {
  836.         numAvailBlocks++;
  837.         if (! List_IsEmpty(fscacheFullWaitList)) {
  838.             Fsutil_WaitListNotify(fscacheFullWaitList);
  839.         }
  840.         Sync_Broadcast(&cleanBlockCondition);
  841.         }
  842.     }
  843.     CacheFileInvalidate(blockPtr->cacheInfoPtr, blockPtr->blockNum, 
  844.                 blockPtr->blockNum);
  845.     UNLOCK_MONITOR;
  846.     return;
  847.     }
  848.  
  849.     if (flags & FSCACHE_CLEAR_READ_AHEAD) {
  850.     blockPtr->flags &= ~FSCACHE_READ_AHEAD_BLOCK;
  851.     }
  852.     if (timeDirtied != 0) {
  853.     if (flags & FSCACHE_BLOCK_BEING_CLEANED) {
  854.         blockPtr->cacheInfoPtr->flags |= FSCACHE_FILE_BEING_CLEANED;
  855.         blockPtr->flags |= FSCACHE_BLOCK_BEING_CLEANED;
  856.     }
  857.     if (!(blockPtr->flags & FSCACHE_BLOCK_DIRTY)) {
  858.         /*
  859.          * Increment the count of dirty blocks if the block isn't marked
  860.          * as dirty.  The block cleaner will decrement the count 
  861.          * after it cleans a block.
  862.          */
  863.         blockPtr->flags |= FSCACHE_BLOCK_DIRTY;
  864.         blockPtr->timeDirtied = timeDirtied;
  865.         if (!(blockPtr->flags & FSCACHE_BLOCK_BEING_WRITTEN)) { 
  866.         blockPtr->cacheInfoPtr->numDirtyBlocks++;
  867.         PutBlockOnDirtyList(blockPtr, FALSE);
  868.         }
  869.     }
  870.     }
  871.     if (diskBlock != -1) {
  872.     blockPtr->diskBlock = diskBlock;
  873.     if (blockPtr->blockSize != blockSize) {
  874.         blockPtr->blockSize = blockSize;
  875.     }
  876.     } else if (blockPtr->blockSize == -1 && blockSize > 0) {
  877.     /*
  878.      * Patch up the block size so our internal fragmentation
  879.      * calculation is correct.  The size of a read-only block
  880.      * is not used for anything else.
  881.      */
  882.     blockPtr->blockSize = blockSize;
  883.     }
  884.  
  885.     blockPtr->refCount--;
  886.     if (blockPtr->refCount == 0) {
  887.     /*
  888.      * Wake up anybody waiting for the block to become unlocked.
  889.      */
  890.     Sync_Broadcast(&blockPtr->ioDone);
  891.     VmMach_UnlockCachePage(blockPtr->blockAddr);
  892.     if (!(blockPtr->flags & 
  893.         (FSCACHE_BLOCK_DIRTY | FSCACHE_BLOCK_BEING_WRITTEN))) {
  894.         numAvailBlocks++;
  895.         if (! List_IsEmpty(fscacheFullWaitList)) {
  896.         Fsutil_WaitListNotify(fscacheFullWaitList);
  897.         }
  898.         Sync_Broadcast(&cleanBlockCondition);
  899.     }
  900.     if (blockPtr->flags & FSCACHE_BLOCK_CLEANER_WAITING) {
  901.         /*
  902.          * Second parameter is for ASPLOS measurements and can be
  903.          * removed after all that's over.  Mary 2/14/92
  904.          */
  905.         StartBackendWriteback(blockPtr->cacheInfoPtr->backendPtr, FALSE);
  906.         blockPtr->flags &= ~FSCACHE_BLOCK_CLEANER_WAITING;
  907.     }
  908.     if (flags & FSCACHE_BLOCK_UNNEEDED) {
  909.         /*
  910.          * This block is unneeded so move it to the front of the LRU list
  911.          * and set its time referenced to zero so that it will be taken
  912.          * at the next convenience.
  913.          */
  914.         if (blockPtr->flags & FSCACHE_BLOCK_DIRTY) {
  915.         blockPtr->flags |= FSCACHE_MOVE_TO_FRONT;
  916.         } else {
  917.         List_Move(&blockPtr->useLinks, LIST_ATFRONT(lruList));
  918.         }
  919.         fs_Stats.blockCache.blocksPitched++;
  920.         blockPtr->timeReferenced = 0;
  921.     } else {
  922.         /*
  923.          * Move it to the end of the lru list, mark it as being referenced. 
  924.          */
  925.         blockPtr->timeReferenced = Fsutil_TimeInSeconds();
  926.         blockPtr->flags &= ~FSCACHE_MOVE_TO_FRONT;
  927.         List_Move( &blockPtr->useLinks, LIST_ATREAR(lruList));
  928.     }
  929.     }
  930.  
  931.     UNLOCK_MONITOR;
  932. }
  933.  
  934.  
  935. /*
  936.  * ----------------------------------------------------------------------------
  937.  *
  938.  * Fscache_BlockTrunc --
  939.  *
  940.  *     Truncate the given cache block.  Used to set the blockSize in the
  941.  *    cache block to reflect the actual amount of data in the block after
  942.  *    a truncate.
  943.  *
  944.  * Results:
  945.  *    None.
  946.  *
  947.  * Side effects:
  948.  *    blockSize in block is changed.
  949.  *
  950.  * ----------------------------------------------------------------------------
  951.  */
  952. ENTRY void
  953. Fscache_BlockTrunc(cacheInfoPtr, blockNum, newBlockSize)
  954.     Fscache_FileInfo *cacheInfoPtr;    /* Cache state of file. */
  955.     int        blockNum;        /* Block to truncate. */
  956.     int        newBlockSize;        /* New block size. */
  957. {
  958.     register Hash_Entry         *hashEntryPtr;
  959.     register Fscache_Block    *blockPtr;
  960.     BlockHashKey         blockHashKey;
  961.  
  962.     LOCK_MONITOR;
  963.  
  964.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, 0);
  965.  
  966.     hashEntryPtr = GetUnlockedBlock(&blockHashKey, blockNum);
  967.     if (hashEntryPtr != (Hash_Entry *) NIL) {
  968.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  969.  
  970.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  971.         panic( "CacheBlockTrunc, hashing error\n");
  972.     } else {
  973.         blockPtr->blockSize = newBlockSize;
  974.     }
  975.     }
  976.  
  977.     UNLOCK_MONITOR;
  978. }
  979.  
  980.  
  981. /*
  982.  * ----------------------------------------------------------------------------
  983.  *
  984.  *    Functions to perform some action on a file.  This includes write-back
  985.  *    and invalidation.
  986.  *
  987.  * ----------------------------------------------------------------------------
  988.  */
  989.  
  990. /*
  991.  * ----------------------------------------------------------------------------
  992.  *
  993.  * Fscache_FileInvalidate --
  994.  *
  995.  *     This function removes from the cache all blocks for the file 
  996.  *    identified by *filePtr in the range firstBlock to lastBlock.
  997.  *
  998.  * Results:
  999.  *    None.
  1000.  *
  1001.  * Side effects:
  1002.  *    All blocks in the cache for the file are removed.
  1003.  *
  1004.  * ----------------------------------------------------------------------------
  1005.  *
  1006.  */
  1007. ENTRY void
  1008. Fscache_FileInvalidate(cacheInfoPtr, firstBlock, lastBlock)
  1009.     Fscache_FileInfo *cacheInfoPtr;    /* Cache state of file to invalidate. */
  1010.     int        firstBlock;    /* First block to invalidate. Starts at zero. */
  1011.     int        lastBlock;    /* Last block to invalidate.  FSCACHE_LAST_BLOCK
  1012.                  * can be used if the caller doesn't know
  1013.                  * the exact last block of the file. */
  1014. {
  1015.     LOCK_MONITOR;
  1016.  
  1017.     if (lastBlock == FSCACHE_LAST_BLOCK) {
  1018.     if (cacheInfoPtr->attr.lastByte > 0) {
  1019.         lastBlock = cacheInfoPtr->attr.lastByte / FS_BLOCK_SIZE;
  1020.     } else {
  1021.         lastBlock = 0;
  1022.     }
  1023.     }
  1024.     CacheFileInvalidate(cacheInfoPtr, firstBlock, lastBlock);
  1025.  
  1026.     UNLOCK_MONITOR;
  1027. }
  1028.  
  1029.  
  1030.  
  1031. /*
  1032.  * ----------------------------------------------------------------------------
  1033.  *
  1034.  * CacheFileInvalidate --
  1035.  *
  1036.  *     This function removes from the cache all blocks for the given file 
  1037.  *    identified in the range firstBlock to lastBlock.  If any blocks are 
  1038.  *    being written to disk, it will block until they have finished being
  1039.  *    written.
  1040.  *
  1041.  * Results:
  1042.  *    None.
  1043.  *
  1044.  * Side effects:
  1045.  *    All blocks in the cache for the file are removed.
  1046.  *
  1047.  * ----------------------------------------------------------------------------
  1048.  */
  1049. INTERNAL static void
  1050. CacheFileInvalidate(cacheInfoPtr, firstBlock, lastBlock)
  1051.     Fscache_FileInfo    *cacheInfoPtr;    /* File to invalidate. */
  1052.     int            firstBlock;    /* First block to invalidate. */
  1053.     int            lastBlock;    /* Last block to invalidate. */
  1054. {
  1055.     register Hash_Entry         *hashEntryPtr;
  1056.     register Fscache_Block    *blockPtr;
  1057.     BlockHashKey         blockHashKey;
  1058.     int                 i;
  1059.  
  1060.  
  1061.     if (cacheInfoPtr->blocksInCache > 0) {
  1062.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, 0);
  1063.  
  1064.     for (i = firstBlock; i <= lastBlock; i++) {
  1065.         hashEntryPtr = GetUnlockedBlock(&blockHashKey, i);
  1066.         if (hashEntryPtr == (Hash_Entry *) NIL) {
  1067.         continue;
  1068.         }
  1069.         blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  1070.         if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  1071.         panic( "CacheFileInvalidate, hashing error\n");
  1072.         continue;
  1073.         }
  1074.  
  1075.         /*
  1076.          * Remove it from the hash table.
  1077.          */
  1078.         cacheInfoPtr->blocksInCache--;
  1079.         List_Remove(&blockPtr->fileLinks);
  1080.         Hash_Delete(blockHashTable, hashEntryPtr);
  1081.  
  1082.         /*
  1083.          * Invalidate the block, including removing it from dirty list
  1084.          * if necessary.
  1085.          */
  1086.         if (blockPtr->flags & FSCACHE_BLOCK_DIRTY) {
  1087.         cacheInfoPtr->numDirtyBlocks--;
  1088.         DeleteBlockFromDirtyList(blockPtr);
  1089.         numAvailBlocks++;
  1090.         }
  1091.         List_Remove(&blockPtr->useLinks);
  1092.         PutOnFreeList(blockPtr);
  1093.     }
  1094.     }
  1095.     if (cacheInfoPtr->blocksInCache == 0) {
  1096.     cacheInfoPtr->flags &=
  1097.             ~(FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE |
  1098.               FSCACHE_DOMAIN_DOWN | FSCACHE_GENERIC_ERROR);
  1099.     }
  1100. }
  1101.  
  1102.  
  1103. /*
  1104.  * ----------------------------------------------------------------------------
  1105.  *
  1106.  * DeleteBlockFromDirtyList --
  1107.  *
  1108.  *     Delete the given block from the dirty list.  This is done when
  1109.  *    the file is being deleted.
  1110.  *
  1111.  * Results:
  1112.  *    None.
  1113.  *
  1114.  * Side effects:
  1115.  *    If this is the last dirty block of a file then the file is
  1116.  *    removed from the file dirty list.  Also, if someone was waiting
  1117.  *    on the block the global writeBackComplete is notified.
  1118.  *
  1119.  * ----------------------------------------------------------------------------
  1120.  */
  1121. INTERNAL static void
  1122. DeleteBlockFromDirtyList(blockPtr)
  1123.     register    Fscache_Block    *blockPtr;
  1124. {
  1125.     register    Fscache_FileInfo    *cacheInfoPtr;
  1126.  
  1127.     cacheInfoPtr = blockPtr->cacheInfoPtr;
  1128.     List_Remove(&blockPtr->dirtyLinks);
  1129.     if ((cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) &&
  1130.         (cacheInfoPtr->numDirtyBlocks == 0) &&
  1131.     !(cacheInfoPtr->flags & FSCACHE_FILE_DESC_DIRTY)) {
  1132.     /*
  1133.      * No more dirty blocks for this file.  Remove the file from the dirty
  1134.      * list and wakeup anyone waiting for the file's list to become
  1135.      * empty.
  1136.      */
  1137.     List_Remove((List_Links *)cacheInfoPtr);
  1138.     cacheInfoPtr->flags &= ~(FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_FSYNC|FSCACHE_FILE_BEING_CLEANED);
  1139.     Sync_Broadcast(&cacheInfoPtr->noDirtyBlocks);
  1140.     }
  1141.  
  1142. }
  1143.  
  1144. /*
  1145.  *----------------------------------------------------------------------
  1146.  *
  1147.  * StartFileSync --
  1148.  *
  1149.  *    Start the process of syncing from the cache to the backend.
  1150.  *
  1151.  * Results:
  1152.  *    None.
  1153.  *
  1154.  * Side effects:
  1155.  *    Backend may be started. File may be moved up the dirty list.
  1156.  *
  1157.  *----------------------------------------------------------------------
  1158.  */
  1159. static void
  1160. StartFileSync(cacheInfoPtr)
  1161.     Fscache_FileInfo    *cacheInfoPtr;
  1162. {
  1163.     register List_Links    *linkPtr;
  1164.     List_Links    *dirtyList, *place;
  1165.  
  1166.     dirtyList = &cacheInfoPtr->backendPtr->dirtyListHdr;
  1167.  
  1168.     /*
  1169.      * If the file has not already been synced, move the file up the
  1170.      * dirty list to the front or right behide the last synced file.
  1171.      */
  1172.     if (!(cacheInfoPtr->flags & FSCACHE_FILE_FSYNC)) {
  1173.     /*
  1174.      * Move down the list until we reach the file or the first non
  1175.      * synced file. 
  1176.      */
  1177.     place = (List_Links *) cacheInfoPtr;
  1178.     LIST_FORALL(dirtyList, linkPtr) {
  1179.         if ((linkPtr == (List_Links *) cacheInfoPtr) ||
  1180.         !(((Fscache_FileInfo *) linkPtr)->flags & FSCACHE_FILE_FSYNC)) {
  1181.         place = linkPtr;
  1182.         break;
  1183.         }
  1184.     }
  1185.     cacheInfoPtr->flags |= FSCACHE_FILE_FSYNC;
  1186.     if (place != (List_Links *) cacheInfoPtr) {
  1187.         List_Move((List_Links *)cacheInfoPtr, LIST_BEFORE(place));
  1188.     }
  1189.     }
  1190.     /*
  1191.      * The second parameter here is just for ASPLOS measurements and can
  1192.      * be removed after the ASPLOS conference (or after the paper is
  1193.      * rejected?).
  1194.      *
  1195.      * Mary 2/14/92
  1196.      */
  1197.     StartBackendWriteback(cacheInfoPtr->backendPtr, TRUE);
  1198. }
  1199.  
  1200.  
  1201. /*
  1202.  * ----------------------------------------------------------------------------
  1203.  *
  1204.  * Fscache_FileWriteBack --
  1205.  *
  1206.  *     This function forces all blocks for the file identified by 
  1207.  *    *hdrPtr in the range firstBlock to lastBlock to disk (or 
  1208.  *    the server).
  1209.  *
  1210.  * Results:
  1211.  *    None.
  1212.  *
  1213.  * Side effects:
  1214.  *    All dirty blocks in the cache for the file are written out.
  1215.  *
  1216.  * ----------------------------------------------------------------------------
  1217.  *
  1218.  */
  1219. ENTRY ReturnStatus
  1220. Fscache_FileWriteBack(cacheInfoPtr, firstBlock, lastBlock, flags,
  1221.     blocksSkippedPtr)
  1222.     register Fscache_FileInfo *cacheInfoPtr;    /* State to force out. */
  1223.     int        firstBlock;    /* First block to write back. */
  1224.     int        lastBlock;    /* Last block to write back. */
  1225.     int        flags;        /* FSCACHE_FILE_WB_WAIT | FSCACHE_WRITE_BACK_INDIRECT |
  1226.                  * FSCACHE_WRITE_BACK_AND_INVALIDATE. 
  1227.                  * FSCACHE_WB_MIGRATION. */
  1228.     int        *blocksSkippedPtr; /* The number of blocks skipped
  1229.                       because they were locked. */
  1230. {
  1231.     register Hash_Entry         *hashEntryPtr;
  1232.     register Fscache_Block    *blockPtr;
  1233.     BlockHashKey         blockHashKey;
  1234.     int                 i;
  1235.     ReturnStatus         status;
  1236.     Boolean             fsyncFile;
  1237.     enum  {ENTIRE_FILE, SINGLE_BLOCK, MULTI_BLOCK_RANGE} rangeType;
  1238.  
  1239.     LOCK_MONITOR;
  1240.  
  1241.     *blocksSkippedPtr = 0;
  1242.  
  1243.     fsyncFile = FALSE;
  1244.     /* 
  1245.      * First classify the type of writeback being requests as either involving
  1246.      * a single file block, the entire file, or a multiple-block range.
  1247.      */
  1248.     if ((firstBlock == lastBlock) && (firstBlock >= 0)) {
  1249.     rangeType = SINGLE_BLOCK;
  1250.     } else if ((firstBlock == 0) && 
  1251.             ((lastBlock == FSCACHE_LAST_BLOCK) || 
  1252.             (lastBlock == cacheInfoPtr->attr.lastByte / FS_BLOCK_SIZE))) {
  1253.     rangeType = ENTIRE_FILE;
  1254.     } else {
  1255.     rangeType = MULTI_BLOCK_RANGE;
  1256.     }
  1257.  
  1258.     /*
  1259.      * Clear out the host down and no disk space flags so we can retry
  1260.      * for this file.
  1261.      */
  1262.     cacheInfoPtr->flags &= 
  1263.             ~(FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE | 
  1264.               FSCACHE_DOMAIN_DOWN | FSCACHE_GENERIC_ERROR);
  1265.  
  1266.     if ((cacheInfoPtr->blocksInCache == 0) ||
  1267.     (flags & FSCACHE_WRITE_BACK_DESC_ONLY)) {
  1268.     /*
  1269.      * If file is on dirty list and has no blocks then it 
  1270.      * descriptor is dirty.  Force the descriptor out 
  1271.      * waiting if the user specified to.
  1272.      */
  1273.     if ((cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) &&
  1274.         (flags & FSCACHE_WRITE_BACK_DESC_ONLY)) {
  1275.         StartFileSync(cacheInfoPtr);
  1276.         if (flags & FSCACHE_FILE_WB_WAIT) {
  1277.         while (cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) {
  1278.             (void) Sync_Wait(&cacheInfoPtr->noDirtyBlocks, FALSE);
  1279.         }
  1280.         }
  1281.     }
  1282.     UNLOCK_MONITOR;
  1283.     return(SUCCESS);
  1284.     }
  1285.  
  1286.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, 0);
  1287.  
  1288.     if (lastBlock == FSCACHE_LAST_BLOCK) {
  1289.     if (cacheInfoPtr->attr.lastByte > 0) {
  1290.         lastBlock = cacheInfoPtr->attr.lastByte / FS_BLOCK_SIZE;
  1291.     } else {
  1292.         lastBlock = 0;
  1293.     }
  1294.     }
  1295.     blockPtr = (Fscache_Block *) NIL;
  1296.     for (i = firstBlock; i <= lastBlock; i++) {
  1297.     /*
  1298.      * See if block is in the hash table.
  1299.      */
  1300.  
  1301.     blockHashKey.blockNumber = i;
  1302. again:
  1303.     hashEntryPtr = Hash_LookOnly(blockHashTable, (Address) &blockHashKey);
  1304.     if (hashEntryPtr == (Hash_Entry *) NIL) {
  1305.         continue;
  1306.     }
  1307.  
  1308.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  1309.  
  1310.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  1311.         panic( "Fscache_FileWriteBack, hashing error\n");
  1312.         UNLOCK_MONITOR;
  1313.         return(FAILURE);
  1314.     }
  1315.  
  1316.     if (flags & (FSCACHE_WRITE_BACK_AND_INVALIDATE | FSCACHE_FILE_WB_WAIT)) {
  1317.         /*
  1318.          * Wait for the block to become unlocked.  If have to wait then
  1319.          * must start over because the block could have been freed while
  1320.          * we were sleeping.
  1321.          */
  1322.         if (blockPtr->refCount > 0) {
  1323.         (void) Sync_Wait(&blockPtr->ioDone, FALSE);
  1324.         if (sys_ShuttingDown) {
  1325.             UNLOCK_MONITOR;
  1326.             return(SUCCESS);
  1327.         }
  1328.         goto again;
  1329.         }
  1330.     } else if (blockPtr->refCount > 0) {
  1331.         /* 
  1332.          * If the block is locked and we are not invalidating it, 
  1333.          * skip the block because it might be being modified.
  1334.          */
  1335.         (*blocksSkippedPtr)++;
  1336.         continue;
  1337.     }
  1338.  
  1339.     if (blockPtr->flags & (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  1340.         fsyncFile = TRUE;
  1341.     }
  1342.     if (flags & FSCACHE_WRITE_BACK_AND_INVALIDATE) {
  1343.         if (blockPtr->flags & 
  1344.             (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  1345.         /*
  1346.          * Mark the block to be release by the block cleaner. We
  1347.          * delete it from the hash table and remove it from the LRU
  1348.          * list for the block cleaner. 
  1349.          */
  1350.         if (!(blockPtr->flags & FSCACHE_BLOCK_DELETED)) { 
  1351.             blockPtr->flags |= FSCACHE_BLOCK_DELETED;
  1352.             Hash_Delete(blockHashTable, hashEntryPtr);
  1353.             List_Remove( &blockPtr->useLinks);
  1354.         }
  1355.         } else {
  1356.         /*
  1357.          * The block is clean and not being written, We remove it
  1358.          * from cache.
  1359.          */
  1360.         cacheInfoPtr->blocksInCache--;
  1361.         List_Remove(&blockPtr->fileLinks);
  1362.         Hash_Delete(blockHashTable, hashEntryPtr);
  1363.         List_Remove(&blockPtr->useLinks);
  1364.         PutOnFreeList(blockPtr);
  1365.         }
  1366.     } 
  1367.     }
  1368.     if ((cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) && fsyncFile) {
  1369.     StartFileSync(cacheInfoPtr);
  1370.     }
  1371.  
  1372.     /*
  1373.      * Wait until all blocks are written back.
  1374.      */
  1375.     if (flags & FSCACHE_FILE_WB_WAIT) {
  1376.      if ((rangeType == SINGLE_BLOCK) && fsyncFile) {
  1377.         while ((blockPtr->flags & 
  1378.         (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) && 
  1379.            !(cacheInfoPtr->flags & 
  1380.             (FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE |
  1381.              FSCACHE_DOMAIN_DOWN | FSCACHE_GENERIC_ERROR)) &&
  1382.            !sys_ShuttingDown) {
  1383.         (void) Sync_Wait(&blockPtr->ioDone, FALSE);
  1384.         }
  1385.      } else { 
  1386.         while ((cacheInfoPtr->flags & 
  1387.             (FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_BEING_WRITTEN)) && 
  1388.            !(cacheInfoPtr->flags & 
  1389.             (FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE |
  1390.              FSCACHE_DOMAIN_DOWN | FSCACHE_GENERIC_ERROR)) &&
  1391.            !sys_ShuttingDown) {
  1392.         (void) Sync_Wait(&cacheInfoPtr->noDirtyBlocks, FALSE);
  1393.         }
  1394.     }
  1395.     }
  1396.  
  1397.     switch (cacheInfoPtr->flags&(FSCACHE_SERVER_DOWN|FSCACHE_NO_DISK_SPACE|
  1398.                  FSCACHE_DOMAIN_DOWN|FSCACHE_GENERIC_ERROR)) {
  1399.     case FSCACHE_SERVER_DOWN:
  1400.         status = RPC_TIMEOUT;
  1401.         break;
  1402.     case FSCACHE_NO_DISK_SPACE:
  1403.         printf("%s couldn't write back <%d,%d> \"%s: %s\n",
  1404.            "Fscache_FileWriteBack",
  1405.            cacheInfoPtr->hdrPtr->fileID.major,
  1406.            cacheInfoPtr->hdrPtr->fileID.major,
  1407.            Fsutil_HandleName(cacheInfoPtr->hdrPtr),
  1408.            "cache entry is marked as no-space"); /* DEBUG */
  1409.         status = FS_NO_DISK_SPACE;
  1410.         break;
  1411.     case FSCACHE_DOMAIN_DOWN:
  1412.         status = FS_DOMAIN_UNAVAILABLE;
  1413.         break;
  1414.     case FSCACHE_GENERIC_ERROR:
  1415.         status = FS_INVALID_ARG;
  1416.         break;
  1417.     default:
  1418.         status = SUCCESS;
  1419.         break;
  1420.     }
  1421.  
  1422.     UNLOCK_MONITOR;
  1423.  
  1424.     return(status);
  1425. }
  1426.  
  1427.  
  1428. /*
  1429.  * ----------------------------------------------------------------------------
  1430.  *
  1431.  * Fscache_BlocksUnneeded --
  1432.  *
  1433.  *    This function moves the blocks that span the given range of bytes to
  1434.  *    the front of the LRU list and marks them as not referenced.  This
  1435.  *    function is called by virtual memory after it has read in an object
  1436.  *    file block that it will cache in a sticky segment.  If we are the
  1437.  *    file server and are being called for an object file, then don't do 
  1438.  *    anything because other clients might need the blocks.
  1439.  *
  1440.  * Results:
  1441.  *    None.
  1442.  *
  1443.  * Side effects:
  1444.  *    None here, see FscacheBlocksUnneeded.
  1445.  *    All blocks that span the given range of bytes are moved to the front of
  1446.  *    the LRU list and marked as not-referenced.
  1447.  *
  1448.  * ----------------------------------------------------------------------------
  1449.  *
  1450.  */
  1451. void
  1452. Fscache_BlocksUnneeded(streamPtr, offset, numBytes, objectFile)
  1453.     register Fs_Stream    *streamPtr;    /* File for which blocks are unneeded.*/
  1454.     int            offset;        /* First byte which is unneeded. */
  1455.     int            numBytes;    /* Number of bytes that are unneeded. */
  1456.     Boolean        objectFile;    /* TRUE if this is for an object 
  1457.                      * file.*/
  1458. {
  1459.     register Fscache_FileInfo *cacheInfoPtr;
  1460.  
  1461.     switch (streamPtr->ioHandlePtr->fileID.type) {
  1462.     case FSIO_LCL_FILE_STREAM: {
  1463.         register Fsio_FileIOHandle *localHandlePtr;
  1464.         if (objectFile) {
  1465.         /*
  1466.          * Keep the blocks cached for remote clients.
  1467.          */
  1468.         return;
  1469.         }
  1470.         localHandlePtr = (Fsio_FileIOHandle *)streamPtr->ioHandlePtr;
  1471.         cacheInfoPtr = &localHandlePtr->cacheInfo;
  1472.         break;
  1473.     }
  1474.     case FSIO_RMT_FILE_STREAM: {
  1475.         register Fsrmt_FileIOHandle *rmtHandlePtr;
  1476.         rmtHandlePtr = (Fsrmt_FileIOHandle *)streamPtr->ioHandlePtr;
  1477.         cacheInfoPtr = &rmtHandlePtr->cacheInfo;
  1478.         break;
  1479.     }
  1480.     default:
  1481.         panic( "Fscache_BlocksUnneeded, bad stream type %d\n",
  1482.         streamPtr->ioHandlePtr->fileID.type);
  1483.         return;
  1484.     }
  1485.     FscacheBlocksUnneeded(cacheInfoPtr, offset, numBytes);
  1486. }
  1487.  
  1488.  
  1489. /*
  1490.  * ----------------------------------------------------------------------------
  1491.  *
  1492.  * FscacheBlocksUnneeded --
  1493.  *
  1494.  *    This function moves the blocks that span the given range of bytes to
  1495.  *    the front of the LRU list and marks them as not referenced.
  1496.  *
  1497.  * Results:
  1498.  *    None.
  1499.  *
  1500.  * Side effects:
  1501.  *    All blocks that span the given range of bytes are moved to the front of
  1502.  *    the LRU list and marked as not-referenced.
  1503.  *
  1504.  * ----------------------------------------------------------------------------
  1505.  *
  1506.  */
  1507. ENTRY void
  1508. FscacheBlocksUnneeded(cacheInfoPtr, offset, numBytes)
  1509.     register Fscache_FileInfo *cacheInfoPtr;    /* Cache state. */
  1510.     int            offset;        /* First byte which is unneeded. */
  1511.     int            numBytes;    /* Number of bytes that are unneeded. */
  1512. {
  1513.     register Hash_Entry        *hashEntryPtr;
  1514.     register Fscache_Block        *blockPtr;
  1515.     BlockHashKey             blockHashKey;
  1516.     int                     i;
  1517.     int                firstBlock;
  1518.     int                lastBlock;
  1519.  
  1520.     LOCK_MONITOR;
  1521.  
  1522.     if (cacheInfoPtr->blocksInCache == 0) {
  1523.     UNLOCK_MONITOR;
  1524.     return;
  1525.     }
  1526.  
  1527.     firstBlock = offset / FS_BLOCK_SIZE;
  1528.     lastBlock = (offset + numBytes - 1) / FS_BLOCK_SIZE;
  1529.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, 0);
  1530.  
  1531.     for (i = firstBlock; i <= lastBlock; i++) {
  1532.     /*
  1533.      * See if block is in the hash table.
  1534.      */
  1535.     blockHashKey.blockNumber = i;
  1536.     hashEntryPtr = Hash_LookOnly(blockHashTable, (Address) &blockHashKey);
  1537.     if (hashEntryPtr == (Hash_Entry *) NIL) {
  1538.         continue;
  1539.     }
  1540.  
  1541.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  1542.  
  1543.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  1544.         panic( "CacheBlocksUnneeded, hashing error\n");
  1545.         continue;
  1546.     }
  1547.  
  1548.     if (blockPtr->refCount > 0) {
  1549.         /*
  1550.          * The block is locked.  This means someone is doing something with
  1551.          * it so we just skip it.
  1552.          */
  1553.         continue;
  1554.     }
  1555.     if (blockPtr->flags & 
  1556.             (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  1557.         /*
  1558.          * Block is being cleaned.  Set the flag so that it will be 
  1559.          * moved to the front after it has been cleaned.
  1560.          */
  1561.         blockPtr->flags |= FSCACHE_MOVE_TO_FRONT;
  1562.     } else {
  1563.         /*
  1564.          * Move the block to the front of the LRU list.
  1565.          */
  1566.         List_Move(&blockPtr->useLinks, LIST_ATFRONT(lruList));
  1567.     }
  1568.     fs_Stats.blockCache.blocksPitched++;
  1569.     /*
  1570.      * Set time referenced to zero so this block will be taken as soon
  1571.      * as needed.
  1572.      */
  1573.     blockPtr->timeReferenced = 0;
  1574.     }
  1575.  
  1576.     UNLOCK_MONITOR;
  1577. }
  1578.  
  1579.  
  1580. /*
  1581.  * ----------------------------------------------------------------------------
  1582.  *
  1583.  *    Functions to perform an action on the entire cache. 
  1584.  *
  1585.  * ----------------------------------------------------------------------------
  1586.  */
  1587.  
  1588. /*
  1589.  * ----------------------------------------------------------------------------
  1590.  *
  1591.  * Fscache_WriteBack --
  1592.  *
  1593.  *    Force all dirty blocks in the cache that were dirtied before
  1594.  *    writeBackTime to disk (or the server).  
  1595.  *
  1596.  * Results:
  1597.  *    None.
  1598.  *
  1599.  * Side effects:
  1600.  *    All dirty blocks in the cache are written out.
  1601.  *
  1602.  * ----------------------------------------------------------------------------
  1603.  *
  1604.  */
  1605. ENTRY void
  1606. Fscache_WriteBack(writeBackTime, blocksSkippedPtr, writeBackAll)
  1607.     unsigned int writeBackTime;       /* Write back all blocks that were 
  1608.                       dirtied before this time. */
  1609.     int        *blocksSkippedPtr; /* The number of blocks skipped
  1610.                       because they were locked. */
  1611.     Boolean    writeBackAll;       /* Write back all files. */
  1612. {
  1613.     LOCK_MONITOR;
  1614.     CacheWriteBack(writeBackTime, blocksSkippedPtr, writeBackAll);
  1615.  
  1616.     UNLOCK_MONITOR;
  1617. }
  1618.  
  1619.  
  1620. /*
  1621.  * ----------------------------------------------------------------------------
  1622.  *
  1623.  * Fscache_Empty --
  1624.  *
  1625.  *    Write back and invalidate all unlocked blocks from the cache.
  1626.  *
  1627.  * Results:
  1628.  *    The number of locked blocks is returned in the argument.
  1629.  *
  1630.  * Side effects:
  1631.  *    All unlocked blocks are written back, if necessary, and invalidated.
  1632.  *
  1633.  * ----------------------------------------------------------------------------
  1634.  *
  1635.  */
  1636. ENTRY void
  1637. Fscache_Empty(numLockedBlocksPtr)
  1638.     int *numLockedBlocksPtr;
  1639. {
  1640.     int             blocksSkipped;
  1641.     register    Fscache_Block    *blockPtr;
  1642.     register    List_Links    *nextPtr, *listPtr;
  1643.  
  1644.     LOCK_MONITOR;
  1645.  
  1646.     *numLockedBlocksPtr = 0;
  1647.     CacheWriteBack(-1, &blocksSkipped, TRUE);
  1648.     listPtr = lruList;
  1649.     nextPtr = List_First(listPtr);
  1650.     while (!List_IsAtEnd(listPtr, nextPtr)) {
  1651.     blockPtr = USE_LINKS_TO_BLOCK(nextPtr);
  1652.     nextPtr = List_Next(nextPtr);
  1653.     if (blockPtr->refCount > 0 || 
  1654.         (blockPtr->flags & 
  1655.         (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN))) {
  1656.         /* 
  1657.          * Skip locked or dirty blocks.
  1658.          */
  1659.         (*numLockedBlocksPtr)++;
  1660.     } else {
  1661.         List_Remove(&blockPtr->useLinks);
  1662.         DeleteBlock(blockPtr);
  1663.         PutOnFreeList(blockPtr);
  1664.     }
  1665.     }
  1666.     UNLOCK_MONITOR;
  1667. }
  1668.  
  1669.  
  1670. /*
  1671.  * ----------------------------------------------------------------------------
  1672.  *
  1673.  * CacheWriteBack --
  1674.  *
  1675.  *    Force all dirty blocks in the cache that were dirtied before
  1676.  *    writeBackTime to disk (or the server).  If writeBackTime equals 
  1677.  *    -1 then all blocks are written back.
  1678.  *
  1679.  * Results:
  1680.  *    None.
  1681.  *
  1682.  * Side effects:
  1683.  *    All dirty blocks in the cache are written out.
  1684.  *
  1685.  * ----------------------------------------------------------------------------
  1686.  *
  1687.  */
  1688. static INTERNAL void
  1689. CacheWriteBack(writeBackTime, blocksSkippedPtr, writeTmpFiles)
  1690.     unsigned int writeBackTime;       /* Write back all blocks that were 
  1691.                       dirtied before this time. */
  1692.     int        *blocksSkippedPtr; /* The number of blocks skipped
  1693.                       because they were locked. */
  1694.     Boolean    writeTmpFiles;       /* TRUE => write-back tmp files even though
  1695.                     *         they are marked as not being
  1696.                     *         written back. */
  1697. {
  1698.     register Fscache_FileInfo    *cacheInfoPtr;
  1699.     int                currentTime;
  1700.     Fscache_Backend        *backendPtr;
  1701.  
  1702.     currentTime = Fsutil_TimeInSeconds();
  1703.  
  1704.     *blocksSkippedPtr = 0;
  1705.     /*
  1706.      * Look thru all the cache backend`s dirty list for files to 
  1707.      * writeback.
  1708.      */
  1709.     filewriteBackTime = writeBackTime;
  1710.     LIST_FORALL(backendList, (List_Links *) backendPtr) {
  1711.     LIST_FORALL(&backendPtr->dirtyListHdr, (List_Links *) cacheInfoPtr) { 
  1712.         /*
  1713.          * Check to see if we should start a block cleaner for this
  1714.          * backend.
  1715.          */
  1716.         if (cacheInfoPtr->flags & (FSCACHE_SERVER_DOWN|FSCACHE_FILE_GONE)) {
  1717.         /*
  1718.          * Don't bother to write-back files for which the server is
  1719.          * down.  These will be written back during recovery.
  1720.          */
  1721.         continue;
  1722.         }
  1723.         if (cacheInfoPtr->flags &
  1724.             (FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  1725.              FSCACHE_GENERIC_ERROR)) {
  1726.         /*
  1727.          * Retry for these types of errors.
  1728.          */
  1729.         if (cacheInfoPtr->lastTimeTried < currentTime) {
  1730.             cacheInfoPtr->flags &=
  1731.             ~(FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  1732.                   FSCACHE_GENERIC_ERROR);
  1733.             StartBackendWriteback(cacheInfoPtr->backendPtr, FALSE);
  1734.             break;
  1735.         } else {
  1736.             continue;
  1737.         }
  1738.         }
  1739.         if ((numAvailBlocks < minNumAvailBlocks + FSCACHE_MIN_BLOCKS) ||
  1740.         (cacheInfoPtr->oldestDirtyBlockTime < writeBackTime) ||
  1741.         (cacheInfoPtr->flags & FSCACHE_FILE_FSYNC)) {
  1742.         StartBackendWriteback(cacheInfoPtr->backendPtr, FALSE);
  1743.         break;
  1744.         }
  1745.     }
  1746.     }
  1747.     /*
  1748.      * Wait for all block cleaners to go idea before returning.
  1749.      */
  1750.     while ((numBackendsActive > 0) && !sys_ShuttingDown) {
  1751.     (void) Sync_Wait(&writeBackComplete, FALSE);
  1752.     }
  1753. }
  1754.  
  1755.  
  1756. /*
  1757.  * ----------------------------------------------------------------------------
  1758.  *
  1759.  *    Functions to create, destroy and allocate cache blocks.  These 
  1760.  *    functions are used to provide the variable sized cache and the LRU
  1761.  *    algorithm for managing cache blocks.
  1762.  *
  1763.  * ----------------------------------------------------------------------------
  1764.  */
  1765.  
  1766. /*
  1767.  * ----------------------------------------------------------------------------
  1768.  *
  1769.  * Fscache_SetMinSize --
  1770.  *
  1771.  *     Set the minimum size of the block cache.  This will entail mapping
  1772.  *    enough blocks so that the number of physical pages in use is greater
  1773.  *    than or equal to the minimum number.
  1774.  *
  1775.  * Results:
  1776.  *    None.
  1777.  *
  1778.  * Side effects:
  1779.  *    More blocks get memory put behind them.
  1780.  *
  1781.  * ----------------------------------------------------------------------------
  1782.  */
  1783. ENTRY void
  1784. Fscache_SetMinSize(minBlocks)
  1785.     int    minBlocks;    /* The minimum number of blocks in the cache. */
  1786. {
  1787.     LOCK_MONITOR;
  1788.  
  1789.  
  1790.  
  1791.     if (minBlocks > fs_Stats.blockCache.maxNumBlocks) {
  1792.     minBlocks = fs_Stats.blockCache.maxNumBlocks;
  1793.     printf( "Fscache_SetMinSize: Only raising min cache size to %d blocks\n", 
  1794.                 minBlocks);
  1795.     }
  1796.     fs_Stats.blockCache.minCacheBlocks = minBlocks;
  1797.     if (fs_Stats.blockCache.minCacheBlocks <= 
  1798.                     fs_Stats.blockCache.numCacheBlocks) {
  1799.     UNLOCK_MONITOR;
  1800.     return;
  1801.     }
  1802.  
  1803.     /*
  1804.      * Give enough blocks memory so that the minimum cache size requirement
  1805.      * is met.
  1806.      */
  1807.     while (fs_Stats.blockCache.numCacheBlocks < 
  1808.                     fs_Stats.blockCache.minCacheBlocks) {
  1809.     if (!CreateBlock(FALSE, (Fscache_Block **) NIL)) {
  1810.         printf("Fscache_SetMinSize: lowered min cache size to %d blocks\n",
  1811.                fs_Stats.blockCache.numCacheBlocks);
  1812.         fs_Stats.blockCache.minCacheBlocks = 
  1813.                     fs_Stats.blockCache.numCacheBlocks;
  1814.     }
  1815.     }
  1816.  
  1817.     UNLOCK_MONITOR;
  1818. }
  1819.  
  1820.  
  1821. /*
  1822.  * ----------------------------------------------------------------------------
  1823.  *
  1824.  * Fscache_SetMaxSize --
  1825.  *
  1826.  *     Set the maximum size of the block cache.  This entails freeing
  1827.  *    enough main memory pages so that the number of cache pages is
  1828.  *    less than the maximum allowed.
  1829.  *
  1830.  * Results:
  1831.  *    None.
  1832.  *
  1833.  * Side effects:
  1834.  *    Cache blocks have memory removed from behind them and are moved to
  1835.  *    the unmapped list. 
  1836.  *
  1837.  * ----------------------------------------------------------------------------
  1838.  */
  1839. ENTRY void
  1840. Fscache_SetMaxSize(maxBlocks)
  1841.     int    maxBlocks;    /* The minimum number of pages in the cache. */
  1842. {
  1843.     Boolean            giveUp;
  1844.     int                pageNum;
  1845.  
  1846.     LOCK_MONITOR;
  1847.  
  1848.     if (maxBlocks > fs_Stats.blockCache.maxNumBlocks) {
  1849.     maxBlocks = fs_Stats.blockCache.maxNumBlocks;
  1850.     printf("Fscache_SetMaxSize: Only raising max cache size to %d blocks\n",
  1851.         maxBlocks);
  1852.     }
  1853.  
  1854.     fs_Stats.blockCache.maxCacheBlocks = maxBlocks;
  1855.     if (fs_Stats.blockCache.maxCacheBlocks >= 
  1856.                     fs_Stats.blockCache.numCacheBlocks) {
  1857.     UNLOCK_MONITOR;
  1858.     return;
  1859.     }
  1860.     
  1861.     /*
  1862.      * Free enough pages to get down to maximum size.
  1863.      */
  1864.     giveUp = FALSE;
  1865.     while (fs_Stats.blockCache.numCacheBlocks > 
  1866.                 fs_Stats.blockCache.maxCacheBlocks && !giveUp) {
  1867.     giveUp = !DestroyBlock(FALSE, &pageNum);
  1868.     }
  1869.  
  1870.  
  1871.     UNLOCK_MONITOR;
  1872. }
  1873.  
  1874.  
  1875. /*
  1876.  * ----------------------------------------------------------------------------
  1877.  *
  1878.  * Fscache_GetPageFromFS --
  1879.  *
  1880.  *     Compare LRU time of the caller to time of block in LRU list and
  1881.  *    if caller has newer pages unmap a block and return a page.
  1882.  *
  1883.  * Results:
  1884.  *    Physical page number if unmap a block.
  1885.  *
  1886.  * Side effects:
  1887.  *    Blocks may be unmapped.
  1888.  *
  1889.  * ----------------------------------------------------------------------------
  1890.  */
  1891. ENTRY void
  1892. Fscache_GetPageFromFS(timeLastAccessed, pageNumPtr)
  1893.     time_t    timeLastAccessed;
  1894.     int        *pageNumPtr;
  1895. {
  1896.     register    Fscache_Block    *blockPtr;
  1897.  
  1898.     LOCK_MONITOR;
  1899.  
  1900.     fs_Stats.blockCache.vmRequests++;
  1901.     *pageNumPtr = -1;
  1902.     if (fs_Stats.blockCache.numCacheBlocks > 
  1903.         fs_Stats.blockCache.minCacheBlocks && !List_IsEmpty(lruList)) {
  1904.     fs_Stats.blockCache.triedToGiveToVM++;
  1905.     blockPtr = (Fscache_Block *) USE_LINKS_TO_BLOCK(List_First(lruList));
  1906.     if (blockPtr->timeReferenced < timeLastAccessed) {
  1907.         fs_Stats.blockCache.vmGotPage++;
  1908.         (void) DestroyBlock(TRUE, pageNumPtr);
  1909.     }
  1910.     }
  1911.  
  1912.     UNLOCK_MONITOR;
  1913. }
  1914.  
  1915. /*
  1916.  * ----------------------------------------------------------------------------
  1917.  *
  1918.  * CreateBlock --
  1919.  *
  1920.  *     Add a new block to the list of free blocks.
  1921.  *
  1922.  * Results:
  1923.  *    None.
  1924.  *
  1925.  * Side effects:
  1926.  *    Block removed from the unmapped list and put onto the free list.
  1927.  *
  1928.  * ----------------------------------------------------------------------------
  1929.  */
  1930. INTERNAL static Boolean
  1931. CreateBlock(retBlock, blockPtrPtr)
  1932.     Boolean        retBlock;    /* TRUE => return a pointer to one of 
  1933.                      * the newly created blocks in 
  1934.                      * *blockPtrPtr. */
  1935.     Fscache_Block    **blockPtrPtr;    /* Where to return pointer to block.
  1936.                      * NIL if caller isn't interested. */
  1937. {
  1938.     register    Fscache_Block    *blockPtr;
  1939.     int                newCachePages;
  1940.  
  1941.     if (List_IsEmpty(unmappedList)) {
  1942.     printf( "CreateBlock: No unmapped blocks\n");
  1943.     return(FALSE);
  1944.     }
  1945.     blockPtr = USE_LINKS_TO_BLOCK(List_First(unmappedList));
  1946.     /*
  1947.      * Put memory behind the first available unmapped cache block.
  1948.      */
  1949.     newCachePages = Vm_MapBlock(blockPtr->blockAddr);
  1950.     if (newCachePages == 0) {
  1951.     return(FALSE);
  1952.     }
  1953.     numAvailBlocks += newCachePages * blocksPerPage;
  1954.     fs_Stats.blockCache.numCacheBlocks += newCachePages * blocksPerPage;
  1955.     /*
  1956.      * If we are told to return a block then take it off of the list of
  1957.      * unmapped blocks and let the caller put it onto the appropriate list.
  1958.      * Otherwise put it onto the free list.
  1959.      */
  1960.     if (retBlock) {
  1961.     List_Remove(&blockPtr->useLinks);
  1962.     *blockPtrPtr = blockPtr;
  1963.     } else {
  1964.     fs_Stats.blockCache.numFreeBlocks++;
  1965.     blockPtr->flags = FSCACHE_BLOCK_FREE;
  1966.     List_Move(&blockPtr->useLinks, LIST_ATREAR(totFreeList));
  1967.     }
  1968.     if (PAGE_IS_8K) {
  1969.     /*
  1970.      * Put the other block in the page onto the appropriate free list.
  1971.      */
  1972.     blockPtr = GET_OTHER_BLOCK(blockPtr);
  1973.     blockPtr->flags = FSCACHE_BLOCK_FREE;
  1974.     fs_Stats.blockCache.numFreeBlocks++;
  1975.     if (retBlock) {
  1976.         List_Move(&blockPtr->useLinks, LIST_ATREAR(partFreeList));
  1977.     } else {
  1978.         List_Move(&blockPtr->useLinks, LIST_ATREAR(totFreeList));
  1979.     }
  1980.     }
  1981.     if (! List_IsEmpty(fscacheFullWaitList)) {
  1982.     Fsutil_WaitListNotify(fscacheFullWaitList);
  1983.     }
  1984.     Sync_Broadcast(&cleanBlockCondition);
  1985.  
  1986.     return(TRUE);
  1987. }
  1988.  
  1989.  
  1990. /*
  1991.  * ----------------------------------------------------------------------------
  1992.  *
  1993.  * DestroyBlock --
  1994.  *
  1995.  *     Destroy one physical page worth of blocks.
  1996.  *
  1997.  * Results:
  1998.  *    None.
  1999.  *
  2000.  * Side effects:
  2001.  *    Cache blocks have memory removed from behind them and are moved to
  2002.  *    the unmapped list. 
  2003.  *
  2004.  * ----------------------------------------------------------------------------
  2005.  */
  2006. INTERNAL static Boolean
  2007. DestroyBlock(retOnePage, pageNumPtr)
  2008.     Boolean    retOnePage;
  2009.     int        *pageNumPtr;
  2010. {
  2011.     register    Fscache_Block    *blockPtr;
  2012.     register    Fscache_Block    *otherBlockPtr;
  2013.     int        pages;
  2014.  
  2015.     if ((numAvailBlocks-blocksPerPage <  minNumAvailBlocks) ||
  2016.         (fs_Stats.blockCache.numCacheBlocks - FSCACHE_MIN_BLOCKS - 
  2017.             blocksPerPage < minNumAvailBlocks) ) {
  2018.     return FALSE;
  2019.     }
  2020.     /*
  2021.      * First try the list of totally free pages.
  2022.      */
  2023.     if (!List_IsEmpty(totFreeList)) {
  2024.     blockPtr = USE_LINKS_TO_BLOCK(List_First(totFreeList));
  2025.     pages = Vm_UnmapBlock(blockPtr->blockAddr, retOnePage,
  2026.                   (unsigned int *)pageNumPtr);
  2027.     fs_Stats.blockCache.numCacheBlocks -= pages * blocksPerPage;
  2028.     blockPtr->flags = FSCACHE_NOT_MAPPED;
  2029.     List_Move(&blockPtr->useLinks, LIST_ATREAR(unmappedList));
  2030.     fs_Stats.blockCache.numFreeBlocks--;
  2031.     if (PAGE_IS_8K) {
  2032.         /*
  2033.          * Unmap the other block.  The block address can point to either
  2034.          * of the two blocks.
  2035.          */
  2036.         blockPtr = GET_OTHER_BLOCK(blockPtr);
  2037.         blockPtr->flags = FSCACHE_NOT_MAPPED;
  2038.         List_Move(&blockPtr->useLinks, LIST_ATREAR(unmappedList));
  2039.         fs_Stats.blockCache.numFreeBlocks--;
  2040.     }
  2041.     numAvailBlocks -= pages * blocksPerPage;
  2042.     if (numAvailBlocks < 0) {
  2043.         panic("DestroyBlock numAvailBlocks < 0.\n");
  2044.     }
  2045.     return(TRUE);
  2046.     }
  2047.  
  2048.     /*
  2049.      * Now take blocks from the LRU list until we get one that we can use.
  2050.      */
  2051.     while (TRUE) {
  2052.     blockPtr = FetchBlock(FALSE, FALSE);
  2053.     if (blockPtr == (Fscache_Block *) NIL) {
  2054.         /*
  2055.          * There are no clean blocks left so give up.
  2056.          */
  2057.         return(FALSE);
  2058.     }
  2059.     if (PAGE_IS_8K) {
  2060.         /*
  2061.          * We have to deal with the other block.  If it is in use, then
  2062.          * we can't take this page.  Otherwise delete the block from
  2063.          * the cache and put it onto the unmapped list.
  2064.          */
  2065.         otherBlockPtr = GET_OTHER_BLOCK(blockPtr);
  2066.         if (otherBlockPtr->refCount > 0 ||
  2067.         (otherBlockPtr->flags & 
  2068.              (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN))) {
  2069.         PutOnFreeList(blockPtr);
  2070.         continue;
  2071.         }
  2072.         /*
  2073.          * The other block is cached but not in use.  Delete it.
  2074.          */
  2075.         if (!(otherBlockPtr->flags & FSCACHE_BLOCK_FREE)) {
  2076.         DeleteBlock(otherBlockPtr);
  2077.         }
  2078.         otherBlockPtr->flags = FSCACHE_NOT_MAPPED;
  2079.         List_Move(&otherBlockPtr->useLinks, LIST_ATREAR(unmappedList));
  2080.     }
  2081.     blockPtr->flags = FSCACHE_NOT_MAPPED;
  2082.     List_Insert(&blockPtr->useLinks, LIST_ATREAR(unmappedList));
  2083.     pages = Vm_UnmapBlock(blockPtr->blockAddr, 
  2084.                 retOnePage, (unsigned int *)pageNumPtr);
  2085.     fs_Stats.blockCache.numCacheBlocks -= pages * blocksPerPage;
  2086.     numAvailBlocks -= pages * blocksPerPage;
  2087.     if (numAvailBlocks < 0) {
  2088.         panic("DestroyBlock numAvailBlocks < 0.\n");
  2089.     }
  2090.     return(TRUE);
  2091.     }
  2092. }
  2093.  
  2094.  
  2095. /*
  2096.  * ----------------------------------------------------------------------------
  2097.  *
  2098.  * FetchBlock --
  2099.  *
  2100.  *    Return a pointer to the oldest available block on the lru list.
  2101.  *    If had to sleep because all of memory is dirty then return NIL.
  2102.  *    In this cause our caller has to retry various free lists.
  2103.  *
  2104.  * Results:
  2105.  *    Pointer to oldest available block, NIL if had to wait.  
  2106.  *
  2107.  * Side effects:
  2108.  *    Block deleted.
  2109.  *
  2110.  * ----------------------------------------------------------------------------
  2111.  */
  2112. static INTERNAL Fscache_Block *
  2113. FetchBlock(canWait, cantBlock)
  2114.     Boolean    canWait;    /* TRUE implies can sleep if all of memory is 
  2115.                  * dirty. */
  2116.     Boolean    cantBlock;    /* TRUE if we can't block. */
  2117. {
  2118.     register    Fscache_Block    *blockPtr;
  2119.     register    List_Links    *listPtr;
  2120.  
  2121.     if (List_IsEmpty(lruList)) {
  2122.     printf("FetchBlock: LRU list is empty\n");
  2123.     return((Fscache_Block *) NIL);
  2124.     }
  2125.  
  2126.     if ((numAvailBlocks > minNumAvailBlocks) || cantBlock)  {
  2127.     /*
  2128.      * Scan list for unlocked, clean block.
  2129.      */
  2130.     LIST_FORALL(lruList, listPtr) {
  2131.         blockPtr = USE_LINKS_TO_BLOCK(listPtr);
  2132.         if (blockPtr->refCount > 0) {
  2133.         /*
  2134.          * Block is locked.
  2135.          */
  2136.         } else if (blockPtr->flags & 
  2137.                 (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  2138.         /*
  2139.          * Block is dirty or being cleaned.  Mark it so that it will be
  2140.          * freed after it has been cleaned.
  2141.          */
  2142.         blockPtr->flags |= FSCACHE_MOVE_TO_FRONT;
  2143.         if (!(blockPtr->cacheInfoPtr->flags & 
  2144.                     FSCACHE_FILE_BEING_WRITTEN)) {
  2145.             StartFileSync(blockPtr->cacheInfoPtr);
  2146.         }
  2147.         } else if (blockPtr->flags & FSCACHE_BLOCK_DELETED) {
  2148.         printf( "FetchBlock: deleted block %d of file %d in LRU list\n",
  2149.             blockPtr->blockNum, blockPtr->fileNum);
  2150.         } else  {
  2151.         /*
  2152.          * This block is clean and unlocked.  Delete it from the
  2153.          * hash table and use it.
  2154.          */
  2155.         fs_Stats.blockCache.lru++;
  2156.         List_Remove(&blockPtr->useLinks);
  2157.         DeleteBlock(blockPtr);
  2158.         return(blockPtr);
  2159.         }
  2160.     }
  2161.     } else if (numAvailBlocks <= minNumAvailBlocks) {
  2162.      Fscache_Backend    *backendPtr;
  2163.          LIST_FORALL(backendList, (List_Links *) backendPtr) {
  2164.         if (!List_IsEmpty(&backendPtr->dirtyListHdr)) { 
  2165.         StartBackendWriteback(backendPtr, FALSE);
  2166.         }
  2167.          }
  2168.   }
  2169.     /*
  2170.      * We have looked at every block but we couldn't use any.
  2171.      * If possible wait until the block cleaner cleans a block for us.
  2172.      */
  2173.     if (canWait && !cantBlock) {
  2174.     (void) Sync_Wait(&cleanBlockCondition, FALSE);
  2175.     }
  2176.     return((Fscache_Block *) NIL);
  2177. }
  2178.  
  2179.  
  2180. /*
  2181.  *----------------------------------------------------------------------
  2182.  *
  2183.  * StartBackendWriteback --
  2184.  *
  2185.  *    Start a backend writeback process for the specified backend.
  2186.  *    This routine keeps track the number of backend writebacks 
  2187.  *    active.
  2188.  *
  2189.  * Results:
  2190.  *    None.
  2191.  *
  2192.  * Side effects:
  2193.  *
  2194.  *----------------------------------------------------------------------
  2195.  */
  2196.  
  2197. static void
  2198. StartBackendWriteback(backendPtr, fileFsynced)
  2199.     Fscache_Backend *backendPtr;
  2200.     Boolean    fileFsynced;
  2201.             /*
  2202.              * Second parameter is for ASPLOS measurements and can be
  2203.              * removed after all that's over.  Mary 2/14/92
  2204.              */
  2205. {
  2206.     Boolean    started;
  2207.  
  2208.     /*
  2209.      * Second parameter is for ASPLOS measurements and can be
  2210.      * removed after all that's over.  Mary 2/14/92
  2211.      */
  2212.     started = backendPtr->ioProcs.startWriteBack(backendPtr, fileFsynced);
  2213.     if (started) {
  2214.     numBackendsActive++;
  2215.     }
  2216.     return;
  2217. }
  2218.  
  2219.  
  2220. /*
  2221.  * ----------------------------------------------------------------------------
  2222.  *
  2223.  *  Fscache_GetDirtyBlock --
  2224.  *
  2225.  *         Take the blocks off of a file's dirty list and return a pointer
  2226.  *    to it.  The synchronization between closing a file and writing
  2227.  *    back its blocks is done here.  If there are no more dirty blocks
  2228.  *    and the file is being closed we poke the closing process.  If
  2229.  *    there are still dirty blocks and someone is closing the file
  2230.  *    we put the file back onto the file dirty list and don't return a block.
  2231.  *
  2232.  * Results:
  2233.  *    The block returned. NIL if no blocks are ready.
  2234.  *
  2235.  * Side effects:
  2236.  *    None.
  2237.  *
  2238.  * ----------------------------------------------------------------------------
  2239.  */
  2240. ENTRY Fscache_Block *
  2241. Fscache_GetDirtyBlock(cacheInfoPtr, blockMatchProc, clientData,
  2242.             lastDirtyBlockPtr)
  2243.     Fscache_FileInfo    *cacheInfoPtr;
  2244.     Boolean        (*blockMatchProc)();
  2245.     ClientData        clientData;
  2246.     int            *lastDirtyBlockPtr;
  2247. {
  2248.     register    List_Links    *dirtyPtr;
  2249.     register    Fscache_Block    *blockPtr;
  2250.  
  2251.     LOCK_MONITOR;
  2252.  
  2253.     *lastDirtyBlockPtr = 0;
  2254.  
  2255.     if (cacheInfoPtr->flags & FSCACHE_CLOSE_IN_PROGRESS) {
  2256.     /*
  2257.      * We can't do any write-backs until the file is closed.
  2258.      * Return zero blocks in hope that the backend will return
  2259.      * the file to the cache.
  2260.      */
  2261.     UNLOCK_MONITOR;
  2262.     return (Fscache_Block *) NIL;
  2263.     }
  2264.     if (cacheInfoPtr->flags & (FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE | 
  2265.                   FSCACHE_GENERIC_ERROR|FSCACHE_FILE_GONE)) {
  2266.     UNLOCK_MONITOR;
  2267.     return (Fscache_Block *) NIL;
  2268.     }
  2269.  
  2270.     LIST_FORALL(&cacheInfoPtr->dirtyList, dirtyPtr) {
  2271.     blockPtr = DIRTY_LINKS_TO_BLOCK(dirtyPtr);
  2272.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  2273.         UNLOCK_MONITOR;
  2274.         panic( "GetDirtyBlock, bad block\n");
  2275.         LOCK_MONITOR;
  2276.         continue;
  2277.     }
  2278.     if (blockPtr->refCount > 0) {
  2279.         /*
  2280.          * Being actively used.  Wait until it is not in use anymore in
  2281.          * case the user is writing it for example.
  2282.          */
  2283.         blockPtr->flags |= FSCACHE_BLOCK_CLEANER_WAITING;
  2284.         continue;
  2285.     }
  2286.     if (!blockMatchProc(blockPtr, clientData)) {
  2287.         continue;
  2288.     }
  2289.     /*
  2290.      * Mark the block as being written out and clear the dirty flag in case
  2291.      * someone modifies it while we are writing it out.
  2292.      */
  2293.     List_Remove(dirtyPtr);
  2294.     blockPtr->flags &= ~(FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_CLEANED);
  2295.     blockPtr->flags |= FSCACHE_BLOCK_BEING_WRITTEN;
  2296.     blockPtr->refCount++;
  2297.     if (blockPtr->refCount == 1) { 
  2298.         VmMach_LockCachePage(blockPtr->blockAddr);
  2299.     }
  2300.     /*
  2301.      * Gather statistics.
  2302.      */
  2303.     fs_Stats.blockCache.blocksWrittenThru++;
  2304.     switch (blockPtr->flags &
  2305.         (FSCACHE_DATA_BLOCK | FSCACHE_IND_BLOCK |
  2306.          FSCACHE_DESC_BLOCK | FSCACHE_DIR_BLOCK)) {
  2307.         case FSCACHE_DATA_BLOCK:
  2308.         fs_Stats.blockCache.dataBlocksWrittenThru++;
  2309.         break;
  2310.         case FSCACHE_IND_BLOCK:
  2311.         fs_Stats.blockCache.indBlocksWrittenThru++;
  2312.         break;
  2313.         case FSCACHE_DESC_BLOCK:
  2314.         fs_Stats.blockCache.descBlocksWrittenThru++;
  2315.         break;
  2316.         case FSCACHE_DIR_BLOCK:
  2317.         fs_Stats.blockCache.dirBlocksWrittenThru++;
  2318.         break;
  2319.         default:
  2320.         printf( "Fscache_GetDirtyBlock: Unknown block type 0x%x\n",
  2321.             blockPtr->flags &
  2322.         (FSCACHE_DATA_BLOCK | FSCACHE_IND_BLOCK |
  2323.          FSCACHE_DESC_BLOCK | FSCACHE_DIR_BLOCK));
  2324.     }
  2325.     if (blockPtr->blockSize < 0) {
  2326.         panic( "Fscache_GetDirtyBlock: uninitialized block size\n");
  2327.     }
  2328.     *lastDirtyBlockPtr = List_IsEmpty(&cacheInfoPtr->dirtyList);
  2329.     UNLOCK_MONITOR;
  2330.     return blockPtr;
  2331.     }
  2332.     *lastDirtyBlockPtr = List_IsEmpty(&cacheInfoPtr->dirtyList);
  2333.     UNLOCK_MONITOR;
  2334.     return (Fscache_Block *) NIL;
  2335. }
  2336.  
  2337.  
  2338. /*
  2339.  * ----------------------------------------------------------------------------
  2340.  *
  2341.  * Fscache_ReturnDirtyBlock --
  2342.  *
  2343.  *         This routine will process the newly cleaned blocks.
  2344.  *
  2345.  * Results:
  2346.  *         None.
  2347.  *
  2348.  * Side effects:
  2349.  *    The block may be moved on the allocate list and also possibly freed.
  2350.  *
  2351.  * ----------------------------------------------------------------------------
  2352.  */
  2353. ENTRY void
  2354. Fscache_ReturnDirtyBlock(blockPtr, status)
  2355.     register  Fscache_Block    *blockPtr; /*  blocks to  return. */
  2356.     ReturnStatus        status;
  2357. {
  2358.     register    Fscache_FileInfo    *cacheInfoPtr;
  2359.  
  2360.     LOCK_MONITOR;
  2361.  
  2362.     cacheInfoPtr = blockPtr->cacheInfoPtr;
  2363.  
  2364.     blockPtr->flags &= ~FSCACHE_BLOCK_BEING_WRITTEN;
  2365.     blockPtr->refCount--;
  2366.     if (blockPtr->refCount == 0) {
  2367.     VmMach_UnlockCachePage(blockPtr->blockAddr);
  2368.     }
  2369.     Sync_Broadcast(&blockPtr->ioDone);
  2370.     if (status == GEN_EINTR) {
  2371.     blockPtr->flags |= FSCACHE_BLOCK_DIRTY;
  2372.     } else if (status != SUCCESS) {
  2373.     /*
  2374.      * This block could not be written out.
  2375.      */
  2376.     Boolean        printErrorMsg;
  2377.  
  2378.     printErrorMsg = FALSE;
  2379.     switch (status) {
  2380.         case RPC_TIMEOUT:
  2381.         case FS_STALE_HANDLE:
  2382.         case RPC_SERVICE_DISABLED:    
  2383.         if (!(cacheInfoPtr->flags & FSCACHE_SERVER_DOWN)) {
  2384.             printErrorMsg = TRUE;
  2385.             cacheInfoPtr->flags |= FSCACHE_SERVER_DOWN;
  2386.         }
  2387.         /*
  2388.          * Mark the handle as needing recovery.  Then invoke a
  2389.          * background process to attempt the recovery now.
  2390.          */
  2391.         (void) Fsutil_WantRecovery(cacheInfoPtr->hdrPtr);
  2392.         if (status == FS_STALE_HANDLE) {
  2393.             Proc_CallFunc(Fsutil_AttemptRecovery,
  2394.                   (ClientData)cacheInfoPtr->hdrPtr, 0);
  2395.         }
  2396.         break;
  2397.         case FS_NO_DISK_SPACE:
  2398.         if (!(cacheInfoPtr->flags & FSCACHE_NO_DISK_SPACE)) {
  2399.             printErrorMsg = TRUE;
  2400.             cacheInfoPtr->flags |= FSCACHE_NO_DISK_SPACE;
  2401.         }
  2402.         break;
  2403.         case FS_DOMAIN_UNAVAILABLE:
  2404.         if (!(cacheInfoPtr->flags & FSCACHE_DOMAIN_DOWN)) {
  2405.             printErrorMsg = TRUE;
  2406.             cacheInfoPtr->flags |= FSCACHE_DOMAIN_DOWN;
  2407.         }
  2408.         break;
  2409.         case DEV_RETRY_ERROR:
  2410.         case DEV_HARD_ERROR:
  2411.         /*
  2412.          * Schedule a background process to allocate new space for
  2413.          * this block.  Inc the ref count so the block won't go away
  2414.          * and won't be written again, and mark it as being written so
  2415.          * noone will attempt to change where the block is on disk.
  2416.          */
  2417.         blockPtr->refCount++;
  2418.         if (blockPtr->refCount == 1) { 
  2419.             VmMach_LockCachePage(blockPtr->blockAddr);
  2420.         }
  2421.         blockPtr->flags |= FSCACHE_BLOCK_BEING_WRITTEN;
  2422.         Proc_CallFunc(
  2423.             cacheInfoPtr->backendPtr->ioProcs.reallocBlock, 
  2424.                     (ClientData)blockPtr, 0);
  2425.         printErrorMsg = TRUE;
  2426.         printf("File blk %d phys blk %d: ",
  2427.                 blockPtr->blockNum, blockPtr->diskBlock);
  2428.         cacheInfoPtr->flags |= FSCACHE_GENERIC_ERROR;
  2429.         break;
  2430.         default: 
  2431.         printErrorMsg = TRUE;
  2432.         cacheInfoPtr->flags |= FSCACHE_GENERIC_ERROR;
  2433.         break;
  2434.     }
  2435.     if (printErrorMsg) {
  2436.         Fsutil_FileError(cacheInfoPtr->hdrPtr, 
  2437.             "Write-back failed", status);
  2438.     }
  2439.     cacheInfoPtr->lastTimeTried = Fsutil_TimeInSeconds();
  2440.     PutBlockOnDirtyList(blockPtr, TRUE);
  2441.     UNLOCK_MONITOR;
  2442.     return;
  2443.     }
  2444.     /*
  2445.      * Successfully wrote the block.
  2446.      */
  2447.     cacheInfoPtr->flags &= 
  2448.             ~(FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE | 
  2449.               FSCACHE_GENERIC_ERROR);
  2450.     if (blockPtr->flags & FSCACHE_BLOCK_DIRTY) { 
  2451.     PutBlockOnDirtyList(blockPtr, TRUE);
  2452.     } else { 
  2453.     if (blockPtr->refCount == 0) {
  2454.         numAvailBlocks++; 
  2455.     }
  2456.     /* 
  2457.      * Now see if we are supposed to take any special action with this
  2458.      * block once we are done.
  2459.      */
  2460.     if (blockPtr->flags & FSCACHE_BLOCK_DELETED) {
  2461.         cacheInfoPtr->blocksInCache--;
  2462.         List_Remove(&blockPtr->fileLinks);
  2463.         PutOnFreeList(blockPtr);
  2464.     } else if (blockPtr->flags & FSCACHE_MOVE_TO_FRONT) {
  2465.         List_Move(&blockPtr->useLinks, LIST_ATFRONT(lruList));
  2466.         blockPtr->flags &= ~FSCACHE_MOVE_TO_FRONT;
  2467.     }
  2468.  
  2469.     cacheInfoPtr->numDirtyBlocks--;
  2470.     /*
  2471.      * Wakeup the block allocator which may be waiting for us to clean
  2472.      * a block
  2473.      */
  2474.     if (! List_IsEmpty(fscacheFullWaitList)) {
  2475.         Fsutil_WaitListNotify(fscacheFullWaitList);
  2476.     }
  2477.     Sync_Broadcast(&cleanBlockCondition);
  2478.     }
  2479.     UNLOCK_MONITOR;
  2480. }
  2481.  
  2482. /*
  2483.  * ----------------------------------------------------------------------------
  2484.  *
  2485.  *  Fscache_GetDirtyFile --
  2486.  *
  2487.  *         Take a dirty file off of the dirty file list for the
  2488.  *    specified backend. Return a pointer to the cache state for 
  2489.  *    the file return. This routine is used by the backend to
  2490.  *    walk through the dirty list.
  2491.  *
  2492.  * Results:
  2493.  *    A pointer to the cache state for the first file on the dirty list.
  2494.  *    If there are no files then a NIL pointer is returned.  
  2495.  *
  2496.  * Side effects:
  2497.  *      An element is removed from the dirty file list.  The fact that
  2498.  *    the dirty list links are at the beginning of the cacheInfo struct
  2499.  *    is well known and relied on to map from the dirty list to cacheInfoPtr.
  2500.  *
  2501.  * ----------------------------------------------------------------------------
  2502.  */
  2503. ENTRY Fscache_FileInfo *
  2504. Fscache_GetDirtyFile(backendPtr, fsyncOnly, fileMatchProc, clientData)
  2505.     Fscache_Backend    *backendPtr;    /* Cache backend to take dirty files
  2506.                      * from. */
  2507.     Boolean        fsyncOnly;    /* TRUE if we should return only 
  2508.                      * fsynced files. */
  2509.     Boolean        (*fileMatchProc)();    /* File match procedure. */
  2510.     ClientData        clientData;    /* ClientData for match procedure. */
  2511. {
  2512.     register Fscache_FileInfo *cacheInfoPtr;
  2513.  
  2514.     LOCK_MONITOR;
  2515.  
  2516.     /*
  2517.      * If the dirty list is empty return NIL.
  2518.      */
  2519.     if (List_IsEmpty(&backendPtr->dirtyListHdr)) {
  2520.     UNLOCK_MONITOR;
  2521.     return (Fscache_FileInfo *) NIL;
  2522.     }
  2523.  
  2524.     /*
  2525.      * Otherwise, search the the dirtyList picking off the file to return.
  2526.      */
  2527.     LIST_FORALL(&(backendPtr->dirtyListHdr), (List_Links *)cacheInfoPtr) {
  2528.      if (cacheInfoPtr->flags & 
  2529.             (FSCACHE_CLOSE_IN_PROGRESS|FSCACHE_SERVER_DOWN)) {
  2530.         /*
  2531.          * Close in progress on the file the block lives in so we aren't
  2532.          * allowed to write any more blocks.
  2533.          */
  2534.         continue;
  2535.     } else if (cacheInfoPtr->flags & FSCACHE_FILE_GONE) {
  2536.         /*
  2537.          * The file is being deleted.
  2538.          */
  2539.         printf("FscacheGetDirtyFile skipping deleted file <%d,%d> \"%s\"\n",
  2540.         cacheInfoPtr->hdrPtr->fileID.major,
  2541.         cacheInfoPtr->hdrPtr->fileID.minor,
  2542.         Fsutil_HandleName(cacheInfoPtr->hdrPtr));
  2543.         continue;
  2544.     } else if (cacheInfoPtr->flags & 
  2545.                (FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  2546.                 FSCACHE_GENERIC_ERROR)) {
  2547.         if (Fsutil_TimeInSeconds() - cacheInfoPtr->lastTimeTried <
  2548.             FSUTIL_WRITE_RETRY_INTERVAL) {
  2549.         continue;
  2550.         }
  2551.         cacheInfoPtr->flags &= 
  2552.                 ~(FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  2553.                   FSCACHE_GENERIC_ERROR);
  2554.     } 
  2555.  
  2556.     if (!fileMatchProc(cacheInfoPtr, clientData)) {
  2557.         continue;
  2558.     }
  2559.     if ((numAvailBlocks > minNumAvailBlocks) && fsyncOnly && 
  2560.         !( (cacheInfoPtr->flags & FSCACHE_FILE_FSYNC) ||
  2561.             (cacheInfoPtr->oldestDirtyBlockTime < filewriteBackTime))) {
  2562.         break;
  2563.     }
  2564.     /*
  2565.      * Check to make sure that if dirty blocks exist then at least one 
  2566.      * of the blocks is available to write.
  2567.      */
  2568.     if (!List_IsEmpty(&cacheInfoPtr->dirtyList)) { 
  2569.         register    List_Links    *dirtyPtr;
  2570.         register    Fscache_Block    *blockPtr;
  2571.         Boolean    found = FALSE;
  2572.         LIST_FORALL(&cacheInfoPtr->dirtyList, dirtyPtr) {
  2573.         blockPtr = DIRTY_LINKS_TO_BLOCK(dirtyPtr);
  2574.         if (blockPtr->refCount == 0) {
  2575.             found = TRUE;
  2576.             break;
  2577.         }
  2578.         }
  2579.         if (!found) {
  2580.         continue;
  2581.         }
  2582.         }
  2583.  
  2584.     cacheInfoPtr->flags |= FSCACHE_FILE_BEING_WRITTEN;
  2585.     cacheInfoPtr->flags &= ~FSCACHE_FILE_ON_DIRTY_LIST;
  2586.     List_Remove((List_Links *)cacheInfoPtr);
  2587.     UNLOCK_MONITOR;
  2588.     return cacheInfoPtr;
  2589.     }
  2590.     UNLOCK_MONITOR;
  2591.     return (Fscache_FileInfo *) NIL;
  2592. }
  2593.  
  2594. /*
  2595.  * ----------------------------------------------------------------------------
  2596.  *
  2597.  *  Fscache_ReturnDirtyFile --
  2598.  *
  2599.  *         Return a file checked-out using get dirty file. 
  2600.  *
  2601.  * Results:
  2602.  * Side effects:
  2603.  *
  2604.  * ----------------------------------------------------------------------------
  2605.  */
  2606. ENTRY void
  2607. Fscache_ReturnDirtyFile(cacheInfoPtr, onFront)
  2608.     Fscache_FileInfo    *cacheInfoPtr;    /* File to return. */
  2609.     Boolean         onFront;    /* Put it on the front the of list. */
  2610. {
  2611.  
  2612.     LOCK_MONITOR;
  2613.  
  2614.     /*
  2615.      * Move the from file being written state (and list) to the
  2616.      * dirty list or no list at all.
  2617.      */
  2618.     cacheInfoPtr->flags &= ~FSCACHE_FILE_BEING_WRITTEN;
  2619.     if (cacheInfoPtr->numDirtyBlocks == 0) { 
  2620.     Sync_Broadcast(&cacheInfoPtr->noDirtyBlocks);
  2621.     if (!(cacheInfoPtr->flags & FSCACHE_FILE_DESC_DIRTY)) {
  2622.         cacheInfoPtr->flags &= ~(FSCACHE_FILE_FSYNC |
  2623.                                  FSCACHE_FILE_BEING_CLEANED);
  2624.     }
  2625.     }
  2626.  
  2627.     if (((cacheInfoPtr->numDirtyBlocks > 0) || 
  2628.          (cacheInfoPtr->flags & FSCACHE_FILE_DESC_DIRTY)) &&
  2629.      !(cacheInfoPtr->flags & FSCACHE_FILE_GONE)) {
  2630.     PutFileOnDirtyList(cacheInfoPtr, 
  2631.                 cacheInfoPtr->oldestDirtyBlockTime);
  2632.     }
  2633.     if (cacheInfoPtr->flags & FSCACHE_CLOSE_IN_PROGRESS) {
  2634.     /*
  2635.      * Wake up anyone waiting for us to finish so that they can 
  2636.      * close their file. 
  2637.      */
  2638.     Sync_Broadcast(&closeCondition);
  2639.     }
  2640.  
  2641.     UNLOCK_MONITOR;
  2642.     return;
  2643. }
  2644.  
  2645. /*
  2646.  *----------------------------------------------------------------------
  2647.  *
  2648.  * FscacheBackendIdle --
  2649.  *
  2650.  *    Inform the cache that a backend write-back has finished..
  2651.  *
  2652.  * Results:
  2653.  *    None.
  2654.  *
  2655.  * Side effects:
  2656.  *
  2657.  *----------------------------------------------------------------------
  2658.  */
  2659. /*ARGSUSED*/
  2660. void
  2661. FscacheBackendIdle(backendPtr)
  2662.     Fscache_Backend *backendPtr;
  2663. {
  2664.     LOCK_MONITOR;
  2665.     numBackendsActive--;
  2666.     if (numBackendsActive == 0) {
  2667.     Sync_Broadcast(&writeBackComplete);
  2668.     }
  2669.     UNLOCK_MONITOR;
  2670.     return;
  2671. }
  2672.  
  2673.  
  2674.  
  2675. /*
  2676.  * ----------------------------------------------------------------------------
  2677.  *
  2678.  * FscacheFinishRealloc --
  2679.  *
  2680.  *    After reallocating new space for a block, finish things up.
  2681.  *
  2682.  * Results:
  2683.  *         None.
  2684.  *
  2685.  * Side effects:
  2686.  *         None.
  2687.  *
  2688.  * ----------------------------------------------------------------------------
  2689.  */
  2690. ENTRY void
  2691. FscacheFinishRealloc(blockPtr, diskBlock)
  2692.     Fscache_Block    *blockPtr;
  2693.     int            diskBlock;
  2694. {
  2695.     LOCK_MONITOR;
  2696.  
  2697.     blockPtr->refCount--;
  2698.     if (blockPtr->refCount == 0) { 
  2699.     VmMach_UnlockCachePage(blockPtr->blockAddr);
  2700.     }
  2701.     blockPtr->flags &= ~FSCACHE_BLOCK_BEING_WRITTEN;
  2702.     Sync_Broadcast(&blockPtr->ioDone);
  2703.     if (diskBlock != -1) {
  2704.     blockPtr->diskBlock = diskBlock;
  2705.     blockPtr->cacheInfoPtr->flags &= 
  2706.                 ~(FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  2707.                   FSCACHE_GENERIC_ERROR);
  2708.     PutFileOnDirtyList(blockPtr->cacheInfoPtr, 
  2709.             blockPtr->cacheInfoPtr->oldestDirtyBlockTime);
  2710.     StartBackendWriteback(blockPtr->cacheInfoPtr->backendPtr, FALSE);
  2711.     }
  2712.  
  2713.     UNLOCK_MONITOR;
  2714. }
  2715.  
  2716.  
  2717. /*
  2718.  * ----------------------------------------------------------------------------
  2719.  *
  2720.  * Fscache_PreventWriteBacks --
  2721.  *
  2722.  *    Mark this file as a close in progress.  This routine will not
  2723.  *    return until all dirty block cleaners are done writing out blocks
  2724.  *    for this file.  This is called before doing a close on a file
  2725.  *    and is needed to synchronize write-backs and closes so the
  2726.  *    file server knows when it has all the dirty blocks of a file.
  2727.  *
  2728.  * Results:
  2729.  *    The number of dirty blocks in the cache for this file.
  2730.  *    -1 is returned if the file is not cacheable.
  2731.  *
  2732.  * Side effects:
  2733.  *    FSCACHE_CLOSE_IN_PROGRESS flags set in the cacheInfo for this file.
  2734.  *
  2735.  * ----------------------------------------------------------------------------
  2736.  *
  2737.  */
  2738. ENTRY int
  2739. Fscache_PreventWriteBacks(cacheInfoPtr)
  2740.     Fscache_FileInfo *cacheInfoPtr;
  2741. {
  2742.     int    numDirtyBlocks;
  2743.  
  2744.     LOCK_MONITOR;
  2745.  
  2746.     cacheInfoPtr->flags |= FSCACHE_CLOSE_IN_PROGRESS;
  2747.     while (cacheInfoPtr->flags & FSCACHE_FILE_BEING_WRITTEN) {
  2748.     (void)Sync_Wait(&closeCondition, FALSE);
  2749.     }
  2750.     if (cacheInfoPtr->flags & FSCACHE_FILE_NOT_CACHEABLE) {
  2751.     numDirtyBlocks = -1;
  2752.     } else {
  2753.     numDirtyBlocks = cacheInfoPtr->numDirtyBlocks;
  2754.     }
  2755.  
  2756.     UNLOCK_MONITOR;
  2757.  
  2758.     return(numDirtyBlocks);
  2759. }
  2760.  
  2761.  
  2762. /*
  2763.  * ----------------------------------------------------------------------------
  2764.  *
  2765.  * Fscache_AllowWriteBacks --
  2766.  *
  2767.  *    The close that was in progress on this file is now done.  We
  2768.  *    can continue to write back blocks now.
  2769.  *
  2770.  * Results:
  2771.  *    None.
  2772.  *
  2773.  * Side effects:
  2774.  *    FSCACHE_CLOSE_IN_PROGRESS flag cleared from the handle for this file.
  2775.  *    Also if the block cleaner is waiting for us then wake it up.
  2776.  *
  2777.  * ----------------------------------------------------------------------------
  2778.  *
  2779.  */
  2780. ENTRY void
  2781. Fscache_AllowWriteBacks(cacheInfoPtr)
  2782.     register    Fscache_FileInfo *cacheInfoPtr;
  2783. {
  2784.     LOCK_MONITOR;
  2785.  
  2786.     cacheInfoPtr->flags &= ~FSCACHE_CLOSE_IN_PROGRESS;
  2787.     if ((cacheInfoPtr->numDirtyBlocks > 0) || 
  2788.         (cacheInfoPtr->flags & FSCACHE_FILE_DESC_DIRTY)) { 
  2789.     PutFileOnDirtyList(cacheInfoPtr, cacheInfoPtr->oldestDirtyBlockTime);
  2790.     if (cacheInfoPtr->flags & FSCACHE_FILE_FSYNC) {
  2791.         StartBackendWriteback(cacheInfoPtr->backendPtr, FALSE);
  2792.     }
  2793.     }
  2794.  
  2795.     UNLOCK_MONITOR;
  2796. }
  2797.  
  2798.  
  2799. /*
  2800.  * ----------------------------------------------------------------------------
  2801.  *
  2802.  * Fscache_PutFileOnDirtyList --
  2803.  *
  2804.  *    Put the specified file on the dirty list.
  2805.  *
  2806.  * Results:
  2807.  *    None.
  2808.  *
  2809.  * Side effects:
  2810.  *
  2811.  * ----------------------------------------------------------------------------
  2812.  *
  2813.  */
  2814. ReturnStatus
  2815. Fscache_PutFileOnDirtyList(cacheInfoPtr, flags)
  2816.     register    Fscache_FileInfo *cacheInfoPtr;
  2817.     int        flags;
  2818. {
  2819.     LOCK_MONITOR;
  2820.     cacheInfoPtr->flags |= flags;
  2821.  
  2822.     PutFileOnDirtyList(cacheInfoPtr, (time_t)Fsutil_TimeInSeconds());
  2823.  
  2824.     UNLOCK_MONITOR;
  2825.     return (SUCCESS);
  2826. }
  2827.  
  2828. /*
  2829.  * ----------------------------------------------------------------------------
  2830.  *
  2831.  * Fscache_RemoveFileFromDirtyList --
  2832.  *
  2833.  *    Remove the specified file on the dirty list.
  2834.  *
  2835.  * Results:
  2836.  *    None.
  2837.  *
  2838.  * Side effects:
  2839.  *
  2840.  * ----------------------------------------------------------------------------
  2841.  *
  2842.  */
  2843. ReturnStatus
  2844. Fscache_RemoveFileFromDirtyList(cacheInfoPtr)
  2845.     register    Fscache_FileInfo *cacheInfoPtr;
  2846. {
  2847.     int            blocksInCache;
  2848.  
  2849.     LOCK_MONITOR;
  2850.     while (cacheInfoPtr->flags & FSCACHE_FILE_BEING_WRITTEN) {
  2851.     Sync_Wait(&cacheInfoPtr->noDirtyBlocks, FALSE);
  2852.     }
  2853.     if (cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) {
  2854.     cacheInfoPtr->flags &= ~(FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_FSYNC|
  2855.             FSCACHE_FILE_BEING_CLEANED);
  2856.     List_Remove((List_Links *)cacheInfoPtr);
  2857.     }
  2858.     blocksInCache = cacheInfoPtr->blocksInCache;
  2859.     UNLOCK_MONITOR;
  2860.  
  2861.     if (blocksInCache != 0) {
  2862.     panic("Fscache_RemoveFileFromDirtyList blocks in cache\n");
  2863.     }
  2864.     return SUCCESS;
  2865. }
  2866.  
  2867.  
  2868.  
  2869. /*
  2870.  * ----------------------------------------------------------------------------
  2871.  *
  2872.  *    Miscellaneous functions.
  2873.  *
  2874.  * ----------------------------------------------------------------------------
  2875.  */
  2876.  
  2877. /*
  2878.  * ----------------------------------------------------------------------------
  2879.  *
  2880.  * FscacheAllBlocksInCache --
  2881.  *
  2882.  *     Return true if all of this files blocks are in the cache.  This
  2883.  *    is used to optimize out read ahead.
  2884.  *
  2885.  * Results:
  2886.  *    TRUE if all blocks for the file are in the cache.
  2887.  *
  2888.  * Side effects:
  2889.  *    None.
  2890.  *
  2891.  * ----------------------------------------------------------------------------
  2892.  */
  2893. ENTRY Boolean
  2894. FscacheAllBlocksInCache(cacheInfoPtr)
  2895.     register    Fscache_FileInfo *cacheInfoPtr;
  2896. {
  2897.     Boolean    result;
  2898.     int        numBlocks;
  2899.  
  2900.     LOCK_MONITOR;
  2901.  
  2902.     fs_Stats.blockCache.allInCacheCalls++;
  2903.     if (cacheInfoPtr->attr.lastByte == -1) {
  2904.     result = TRUE;
  2905.     } else {
  2906.     if (cacheInfoPtr->attr.firstByte == -1) {
  2907.         cacheInfoPtr->attr.firstByte = 0;
  2908.     }
  2909.     numBlocks = (cacheInfoPtr->attr.lastByte/FS_BLOCK_SIZE) -
  2910.             (cacheInfoPtr->attr.firstByte/FS_BLOCK_SIZE) + 1;
  2911.     result = (numBlocks == cacheInfoPtr->blocksInCache);
  2912.     if (result) {
  2913.         fs_Stats.blockCache.allInCacheTrue++;
  2914.     }
  2915.     }
  2916.  
  2917.     UNLOCK_MONITOR;
  2918.  
  2919.     return(result);
  2920. }
  2921.  
  2922. /*
  2923.  * ----------------------------------------------------------------------------
  2924.  *
  2925.  * Fscache_ReserveBlocks --
  2926.  *
  2927.  *   Insure that at least the specified number of cache blocks will be 
  2928.  *   available to Fetch_Block() calls the the FSCACHE_CANT_BLOCK flag.
  2929.  *
  2930.  * Results:
  2931.  *    The number of blocks made available.
  2932.  *
  2933.  * Side effects:
  2934.  *    None.
  2935.  *
  2936.  * ----------------------------------------------------------------------------
  2937.  */
  2938. /* ARGSUSED */
  2939. ENTRY int
  2940. Fscache_ReserveBlocks(backendPtr, numResBlocks, numNonResBlocks)
  2941.     Fscache_Backend    *backendPtr; /* unused */
  2942.     int            numResBlocks;
  2943.     int            numNonResBlocks;
  2944. {
  2945.     int    numBlocks = numResBlocks + numNonResBlocks;
  2946.  
  2947.     LOCK_MONITOR;
  2948.     if (numBlocks > fs_Stats.blockCache.maxNumBlocks) {
  2949.     numBlocks = fs_Stats.blockCache.maxNumBlocks - minNumAvailBlocks;
  2950.     }
  2951.  
  2952.     while (fs_Stats.blockCache.numCacheBlocks < minNumAvailBlocks + numBlocks) { 
  2953.     if (!CreateBlock(FALSE, (Fscache_Block **) NIL)) {
  2954.         break;
  2955.     } 
  2956.     }
  2957.     if (minNumAvailBlocks + numResBlocks > 
  2958.         fs_Stats.blockCache.numCacheBlocks - numNonResBlocks) {
  2959.     numResBlocks = fs_Stats.blockCache.numCacheBlocks - numNonResBlocks -
  2960.             minNumAvailBlocks;
  2961.     if (numResBlocks< 0) {
  2962.         numResBlocks = 0;
  2963.     }
  2964.     }
  2965.     minNumAvailBlocks += numResBlocks;
  2966.     while (minNumAvailBlocks > numAvailBlocks) {
  2967.     (void) Sync_Wait(&cleanBlockCondition, FALSE);
  2968.     }
  2969.     UNLOCK_MONITOR;
  2970.     return numResBlocks;
  2971. }
  2972.  
  2973.  
  2974. /*
  2975.  * ----------------------------------------------------------------------------
  2976.  *
  2977.  * Fscache_ReleaseReserveBlocks
  2978.  *
  2979.  *   Release blocks that were Reserved.
  2980.  *
  2981.  * Results:
  2982.  *    None.
  2983.  *
  2984.  * Side effects:
  2985.  *    None.
  2986.  *
  2987.  * ----------------------------------------------------------------------------
  2988.  */
  2989. /* ARGSUSED */
  2990. void
  2991. Fscache_ReleaseReserveBlocks(backendPtr, numBlocks)
  2992.     Fscache_Backend    *backendPtr; /* unused */
  2993.     int            numBlocks;
  2994. {
  2995.     LOCK_MONITOR;
  2996.  
  2997.     minNumAvailBlocks  -= numBlocks;
  2998.     Sync_Broadcast(&cleanBlockCondition);
  2999.  
  3000.     UNLOCK_MONITOR;
  3001.     return;
  3002. }
  3003.  
  3004.  
  3005. /*
  3006.  * ----------------------------------------------------------------------------
  3007.  *
  3008.  * FscacheBlockOkToScavenge --
  3009.  *
  3010.  *    Decide if it is safe to scavenge the file handle.  This is
  3011.  *    called from Fscache_OkToScavenge which
  3012.  *    has already grabbed the per-file cache lock.
  3013.  *
  3014.  * Results:
  3015.  *    TRUE if there are no blocks (clean or dirty) in the cache for this file.
  3016.  *
  3017.  * Side effects:
  3018.  *    None.
  3019.  *
  3020.  * ----------------------------------------------------------------------------
  3021.  */
  3022. ENTRY int
  3023. FscacheBlockOkToScavenge(cacheInfoPtr)
  3024.     register Fscache_FileInfo    *cacheInfoPtr;    /* Cache state to check. */
  3025. {
  3026.     register int numBlocks;
  3027.     register int numBlocksCheck = 0;
  3028.     register Boolean ok;
  3029.     register Fscache_Block *blockPtr;
  3030.     List_Links        *linkPtr;
  3031.  
  3032.     LOCK_MONITOR;
  3033.     numBlocks = cacheInfoPtr->blocksInCache;
  3034.     LIST_FORALL(&cacheInfoPtr->blockList, (List_Links *)linkPtr) {
  3035.     blockPtr = FILE_LINKS_TO_BLOCK(linkPtr);
  3036.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  3037.         UNLOCK_MONITOR;
  3038.         panic( "FsCacheFileBlocks, bad block\n");
  3039.         return(FALSE);
  3040.     }
  3041.     numBlocksCheck++;
  3042.     }
  3043.     LIST_FORALL(&cacheInfoPtr->indList, (List_Links *)linkPtr) {
  3044.     numBlocksCheck++;
  3045.     }
  3046.     if (numBlocksCheck != numBlocks) {
  3047.     UNLOCK_MONITOR;
  3048.     panic( "FsCacheFileBlocks, wrong block count\n");
  3049.     return(FALSE);
  3050.     }
  3051.     ok = (numBlocks == 0) &&
  3052.     ((cacheInfoPtr->flags & 
  3053.         (FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_BEING_WRITTEN)) == 0);
  3054.     UNLOCK_MONITOR;
  3055.     return(ok);
  3056. }
  3057.  
  3058. /*
  3059.  * ----------------------------------------------------------------------------
  3060.  *
  3061.  * FscacheBlockOkToScavengeExceptDirty --
  3062.  *
  3063.  *    Decide if it is safe not to reopen the file handle.  This is
  3064.  *    called from Fscache_OkToScavengeExceptDirty which
  3065.  *    has already grabbed the per-file cache lock.
  3066.  *
  3067.  * Results:
  3068.  *    TRUE if there are no dirty blocks (dirty) in the cache for this file.
  3069.  *
  3070.  * Side effects:
  3071.  *    None.
  3072.  *
  3073.  * ----------------------------------------------------------------------------
  3074.  */
  3075. ENTRY int
  3076. FscacheBlockOkToScavengeExceptDirty(cacheInfoPtr)
  3077.     register Fscache_FileInfo    *cacheInfoPtr;    /* Cache state to check. */
  3078. {
  3079.     register int numBlocks;
  3080.     register int numBlocksCheck = 0;
  3081.     register Boolean ok;
  3082.     register Fscache_Block *blockPtr;
  3083.     List_Links        *linkPtr;
  3084.  
  3085.     LOCK_MONITOR;
  3086.     numBlocks = cacheInfoPtr->blocksInCache;
  3087.     LIST_FORALL(&cacheInfoPtr->blockList, (List_Links *)linkPtr) {
  3088.     blockPtr = FILE_LINKS_TO_BLOCK(linkPtr);
  3089.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  3090.         UNLOCK_MONITOR;
  3091.         panic( "FsCacheFileBlocks, bad block\n");
  3092.         return(FALSE);
  3093.     }
  3094.     numBlocksCheck++;
  3095.     }
  3096.     LIST_FORALL(&cacheInfoPtr->indList, (List_Links *)linkPtr) {
  3097.     numBlocksCheck++;
  3098.     }
  3099.     if (numBlocksCheck != numBlocks) {
  3100.     UNLOCK_MONITOR;
  3101.     panic( "FsCacheFileBlocks, wrong block count\n");
  3102.     return(FALSE);
  3103.     }
  3104.     /*
  3105.      * If there are only clean blocks in the file and we're not supposed
  3106.      * to reopen such files, invalidate the clean blocks.
  3107.      */
  3108.     if (recov_IgnoreCleanFiles && !(cacheInfoPtr->flags &
  3109.         (FSCACHE_FILE_ON_DIRTY_LIST | FSCACHE_FILE_BEING_WRITTEN))) {
  3110.     Fscache_FileInvalidate(cacheInfoPtr, 0, FSCACHE_LAST_BLOCK);
  3111.     numBlocks = cacheInfoPtr->blocksInCache;
  3112.     }
  3113.     /*
  3114.      * If there are only clean blocks in the cache and we're supposed to
  3115.      * skip reopening them (but not invalidate them) pretend they have
  3116.      * zero blocks.
  3117.      */
  3118.     if (recov_SkipCleanFiles && !(cacheInfoPtr->flags &
  3119.         (FSCACHE_FILE_ON_DIRTY_LIST | FSCACHE_FILE_BEING_WRITTEN))) {
  3120.     numBlocks = 0;
  3121.     }
  3122.  
  3123.     ok = (numBlocks == 0) &&
  3124.     ((cacheInfoPtr->flags & 
  3125.         (FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_BEING_WRITTEN)) == 0);
  3126.     UNLOCK_MONITOR;
  3127.     return(ok);
  3128. }
  3129.  
  3130. /*
  3131.  * ----------------------------------------------------------------------------
  3132.  *
  3133.  * Fscache_FileInfoSyncLockCleanup --
  3134.  *
  3135.  *     Clean up Sync_Lock tracing for the cache lock.
  3136.  *
  3137.  * Results:
  3138.  *    None.
  3139.  *
  3140.  * Side effects:
  3141.  *    Set up the fields of the Fscache_FileInfo struct.
  3142.  *
  3143.  * ----------------------------------------------------------------------------
  3144.  */
  3145. /*ARGSUSED*/
  3146. void
  3147. Fscache_FileInfoSyncLockCleanup(cacheInfoPtr)
  3148.     Fscache_FileInfo *cacheInfoPtr;
  3149. {
  3150.     Sync_LockClear(&cacheInfoPtr->lock);
  3151. }
  3152.  
  3153.  
  3154. /*
  3155.  * ----------------------------------------------------------------------------
  3156.  * 
  3157.  *    List Functions
  3158.  *
  3159.  * Functions to put objects onto the free and dirty lists.
  3160.  *
  3161.  * ----------------------------------------------------------------------------
  3162.  */
  3163.  
  3164. /*
  3165.  * ----------------------------------------------------------------------------
  3166.  *
  3167.  * PutOnFreeList --
  3168.  *
  3169.  *     Put the given block onto one of the two free lists.
  3170.  *
  3171.  * Results:
  3172.  *    None.
  3173.  *
  3174.  * Side effects:
  3175.  *    Block either added to the partially free list or the totally free list.
  3176.  *    The list of processes waiting on the full cache is notified if
  3177.  *    is non-empty.
  3178.  *
  3179.  * ----------------------------------------------------------------------------
  3180.  */
  3181. INTERNAL static void
  3182. PutOnFreeList(blockPtr)
  3183.     register    Fscache_Block    *blockPtr;
  3184. {
  3185.     register    Fscache_Block    *otherBlockPtr;
  3186.  
  3187.     blockPtr->flags = FSCACHE_BLOCK_FREE;
  3188.     fs_Stats.blockCache.numFreeBlocks++;
  3189.     if (PAGE_IS_8K) {
  3190.     /*
  3191.      * If all blocks in the page are free then put this block onto the
  3192.      * totally free list.  Otherwise it goes onto the partially free list.
  3193.      */
  3194.     otherBlockPtr = GET_OTHER_BLOCK(blockPtr);
  3195.     if (otherBlockPtr->flags & FSCACHE_BLOCK_FREE) {
  3196.         List_Insert(&blockPtr->useLinks, LIST_ATFRONT(totFreeList));
  3197.         List_Move(&otherBlockPtr->useLinks, LIST_ATFRONT(totFreeList));
  3198.     } else {
  3199.         List_Insert(&blockPtr->useLinks, LIST_ATFRONT(partFreeList));
  3200.     }
  3201.     } else {
  3202.     List_Insert(&blockPtr->useLinks, LIST_ATFRONT(totFreeList));
  3203.     }
  3204.     if (! List_IsEmpty(fscacheFullWaitList)) {
  3205.     Fsutil_WaitListNotify(fscacheFullWaitList);
  3206.     }
  3207.     Sync_Broadcast(&cleanBlockCondition);
  3208. }    
  3209.  
  3210.  
  3211. /*
  3212.  * ----------------------------------------------------------------------------
  3213.  *
  3214.  * PutFileOnDirtyList --
  3215.  *
  3216.  *     Put the given file onto it's backend's dirty list.
  3217.  *
  3218.  * Results:
  3219.  *    None.
  3220.  *
  3221.  * Side effects:
  3222.  *    File's backend's dirty list is modified.
  3223.  *
  3224.  * ----------------------------------------------------------------------------
  3225.  */
  3226. INTERNAL static void
  3227. PutFileOnDirtyList(cacheInfoPtr, oldestDirtyBlockTime)
  3228.     register Fscache_FileInfo    *cacheInfoPtr;    /* Cache info for a file */
  3229.     time_t    oldestDirtyBlockTime;
  3230. {
  3231.     register List_Links    *linkPtr;
  3232.     List_Links    *dirtyList, *place;
  3233.  
  3234.     dirtyList = &cacheInfoPtr->backendPtr->dirtyListHdr;
  3235.     if (!(cacheInfoPtr->flags & 
  3236.     (FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_BEING_WRITTEN))) {
  3237.         List_Insert((List_Links *)cacheInfoPtr, LIST_ATREAR(dirtyList));
  3238.     if (cacheInfoPtr->flags & FSCACHE_FILE_FSYNC) {
  3239.         /*
  3240.          * Move down the list until we reach the file or the first non
  3241.          * synced file. 
  3242.          */
  3243.         place = (List_Links *) cacheInfoPtr;
  3244.         LIST_FORALL(dirtyList, linkPtr) {
  3245.         if ((linkPtr == (List_Links *) cacheInfoPtr) ||
  3246.             !(((Fscache_FileInfo *) linkPtr)->flags & 
  3247.                     FSCACHE_FILE_FSYNC)) {
  3248.             place = linkPtr;
  3249.             break;
  3250.         }
  3251.         }
  3252.         if (place != (List_Links *) cacheInfoPtr) {
  3253.         List_Move((List_Links *)cacheInfoPtr, LIST_BEFORE(place));
  3254.         }
  3255.     }
  3256.     cacheInfoPtr->oldestDirtyBlockTime = oldestDirtyBlockTime;
  3257.     cacheInfoPtr->flags |= FSCACHE_FILE_ON_DIRTY_LIST;
  3258.     }
  3259. }
  3260.  
  3261.  
  3262. /*
  3263.  * ----------------------------------------------------------------------------
  3264.  *
  3265.  * PutBlockOnDirtyList --
  3266.  *
  3267.  *     Put the given block onto the dirty list for the file, and make
  3268.  *    sure that the file is on the list of dirty files.
  3269.  *
  3270.  * Results:
  3271.  *    None.
  3272.  *
  3273.  * Side effects:
  3274.  *    The block goes onto the dirty list of the file.
  3275.  *
  3276.  * ----------------------------------------------------------------------------
  3277.  */
  3278. INTERNAL static void
  3279. PutBlockOnDirtyList(blockPtr, onFront)
  3280.     register    Fscache_Block    *blockPtr;    /* Block to put on list. */
  3281.     Boolean    onFront;            /* Put block on front not
  3282.                          * rear of list. */
  3283. {
  3284.     register Fscache_FileInfo *cacheInfoPtr = blockPtr->cacheInfoPtr;
  3285.  
  3286.     blockPtr->flags |= FSCACHE_BLOCK_DIRTY;
  3287.     List_Insert(&blockPtr->dirtyLinks, 
  3288.         onFront ? LIST_ATFRONT(&cacheInfoPtr->dirtyList) :
  3289.               LIST_ATREAR(&cacheInfoPtr->dirtyList));
  3290.  
  3291.     PutFileOnDirtyList(cacheInfoPtr, blockPtr->timeDirtied);
  3292. }
  3293.  
  3294.  
  3295.  
  3296.  
  3297. /*
  3298.  * ----------------------------------------------------------------------------
  3299.  *
  3300.  * GetUnlockedBlock --
  3301.  *
  3302.  *    Retrieve a block from the hash table.  This routine will not return
  3303.  *    until the block is unlocked and is not being written.
  3304.  *
  3305.  * Results:
  3306.  *    Pointer to hash table entry for the block.
  3307.  *
  3308.  * Side effects:
  3309.  *    None.
  3310.  *
  3311.  * ----------------------------------------------------------------------------
  3312.  */
  3313.  
  3314. INTERNAL static Hash_Entry *
  3315. GetUnlockedBlock(blockHashKeyPtr, blockNum)
  3316.     register    BlockHashKey    *blockHashKeyPtr;
  3317.     int                blockNum;
  3318. {
  3319.     register    Fscache_Block    *blockPtr;
  3320.     register    Hash_Entry    *hashEntryPtr;
  3321.  
  3322.     /*
  3323.      * See if block is in the hash table.
  3324.      */
  3325.     blockHashKeyPtr->blockNumber = blockNum;
  3326. again:
  3327.     hashEntryPtr = Hash_LookOnly(blockHashTable, (Address)blockHashKeyPtr);
  3328.     if (hashEntryPtr == (Hash_Entry *) NIL) {
  3329.     return((Hash_Entry *) NIL);
  3330.     }
  3331.  
  3332.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  3333.     /*
  3334.      * Wait until the block is unlocked.  Once wake up start over because
  3335.      * the block could have been freed while we were asleep.
  3336.      */
  3337.     if (blockPtr->refCount > 0 || 
  3338.     (blockPtr->flags & FSCACHE_BLOCK_BEING_WRITTEN)) {
  3339.     (void) Sync_Wait(&blockPtr->ioDone, FALSE);
  3340.     if (sys_ShuttingDown) {
  3341.         return((Hash_Entry *) NIL);
  3342.     }
  3343.     goto again;
  3344.     }
  3345.     return(hashEntryPtr);
  3346. }
  3347.  
  3348.  
  3349. /*
  3350.  * ----------------------------------------------------------------------------
  3351.  *
  3352.  * DeleteBlock --
  3353.  *
  3354.  *    Remove the block from the hash table.
  3355.  *
  3356.  * Results:
  3357.  *    None.    
  3358.  *
  3359.  * Side effects:
  3360.  *    Decrement count of blocks in cache for file and block deleted from
  3361.  *    hash table.
  3362.  *
  3363.  * ----------------------------------------------------------------------------
  3364.  *
  3365.  */
  3366. static Fscache_Block *deletedBlockPtr;
  3367.  
  3368. INTERNAL static void
  3369. DeleteBlock(blockPtr)
  3370.     register    Fscache_Block    *blockPtr;
  3371. {
  3372.     BlockHashKey    blockHashKey;
  3373.     register Hash_Entry    *hashEntryPtr;
  3374.  
  3375.     SET_BLOCK_HASH_KEY(blockHashKey, blockPtr->cacheInfoPtr,
  3376.                      blockPtr->blockNum);
  3377.     hashEntryPtr = Hash_LookOnly(blockHashTable, (Address) &blockHashKey);
  3378.     if (hashEntryPtr == (Hash_Entry *) NIL) {
  3379.     UNLOCK_MONITOR;
  3380.     deletedBlockPtr = blockPtr;
  3381.     panic("DeleteBlock: Block in LRU list is not in the hash table.\n");
  3382.     LOCK_MONITOR;
  3383.     return;
  3384.     }
  3385.     Hash_Delete(blockHashTable, hashEntryPtr);
  3386.     blockPtr->cacheInfoPtr->blocksInCache--;
  3387.     List_Remove(&blockPtr->fileLinks);
  3388. }
  3389.  
  3390.  
  3391. /*
  3392.  * ----------------------------------------------------------------------------
  3393.  *
  3394.  * Fscache_DumpStats --
  3395.  *
  3396.  *    Print out the cache statistics.
  3397.  *
  3398.  * Results:
  3399.  *         None.
  3400.  *
  3401.  * Side effects:
  3402.  *         None.
  3403.  *
  3404.  * ----------------------------------------------------------------------------
  3405.  */
  3406. /*ARGSUSED*/ 
  3407. void
  3408. Fscache_DumpStats(dummy)
  3409.     ClientData dummy;        /* unused; see dump.c:eventTable */
  3410. {
  3411.     register Fs_BlockCacheStats *block;
  3412.  
  3413.     block = &fs_Stats.blockCache;
  3414.  
  3415.     printf("\n");
  3416.     printf("READ  %d dirty hits %d clean hits %d zero fill %d\n",
  3417.         block->readAccesses,
  3418.         block->readHitsOnDirtyBlock,
  3419.         block->readHitsOnCleanBlock,
  3420.         block->readZeroFills);
  3421.     printf("WRITE %d p-hits %d p-misses %d thru %d zero %d/%d app %d over %d\n",
  3422.         block->writeAccesses,
  3423.         block->partialWriteHits,
  3424.         block->partialWriteMisses,
  3425.         block->blocksWrittenThru,
  3426.         block->writeZeroFills1, block->writeZeroFills2,
  3427.         block->appendWrites,
  3428.         block->overWrites);
  3429.     if (block->fragAccesses != 0) {
  3430.     printf("FRAG upgrades %d hits %d zero fills\n",
  3431.             block->fragAccesses,
  3432.             block->fragHits,
  3433.             block->fragZeroFills);
  3434.     }
  3435.     if (block->fileDescReads != 0) {
  3436.     printf("FILE DESC reads %d hits %d writes %d hits %d\n", 
  3437.             block->fileDescReads, block->fileDescReadHits,
  3438.             block->fileDescWrites, block->fileDescWriteHits);
  3439.     }
  3440.     if (block->indBlockAccesses != 0) {
  3441.     printf("INDIRECT BLOCKS Accesses %d hits %d\n", 
  3442.             block->indBlockAccesses, block->indBlockHits);
  3443.     }
  3444.     printf("VM asked %d, we tried %d, gave up %d\n",
  3445.         block->vmRequests, block->triedToGiveToVM, block->vmGotPage);
  3446.     printf("BLOCK free %d new %d lru %d part free %d\n",
  3447.         block->totFree, block->unmapped,
  3448.         block->lru, block->partFree);
  3449.     printf("SIZES Blocks min %d num %d max %d, Blocks max %d free %d pitched %d\n",
  3450.         block->minCacheBlocks, block->numCacheBlocks,
  3451.         block->maxCacheBlocks, block->maxNumBlocks,
  3452.         block->numFreeBlocks, block->blocksPitched);
  3453.  
  3454.     printf("OBJECTS stream %d (clt %d) file %d dir %d rmtFile %d pipe %d\n",
  3455.         fs_Stats.object.streams, fs_Stats.object.streamClients,
  3456.         fs_Stats.object.files, fs_Stats.object.directory,
  3457.         fs_Stats.object.rmtFiles, fs_Stats.object.pipes);
  3458.     printf("OBJECTS dev %d pdevControl %d pdev %d remote %d Total %d\n",
  3459.         fs_Stats.object.devices, fs_Stats.object.controls,
  3460.         fs_Stats.object.pseudoStreams, fs_Stats.object.remote,
  3461.         fs_Stats.object.streams + fs_Stats.object.files +
  3462.         fs_Stats.object.rmtFiles + fs_Stats.object.pipes +
  3463.         fs_Stats.object.devices + fs_Stats.object.controls +
  3464.         fs_Stats.object.directory +
  3465.         2 * fs_Stats.object.pseudoStreams + fs_Stats.object.remote);
  3466.     printf("HANDLES max %d exist %d. In %d scans replaced %d of %d (dirs %d)\n",
  3467.         fs_Stats.handle.maxNumber, fs_Stats.handle.exists,
  3468.         fs_Stats.handle.lruScans, fs_Stats.handle.lruHits,
  3469.         fs_Stats.handle.lruChecks, fs_Stats.object.dirFlushed);
  3470. }
  3471.  
  3472.  
  3473. /*
  3474.  * ----------------------------------------------------------------------------
  3475.  *
  3476.  * Fscache_CheckFragmentation --
  3477.  *
  3478.  *    Scan through the cache determining the number of bytes wasted
  3479.  *    compared to a fully variable cache and a cache with 1024 byte blocks.
  3480.  *
  3481.  * Results:
  3482.  *    The number of blocks in the cache, number of bytes wasted.
  3483.  *
  3484.  * Side effects:
  3485.  *    None.
  3486.  *
  3487.  * ----------------------------------------------------------------------------
  3488.  *
  3489.  */
  3490. ENTRY void
  3491. Fscache_CheckFragmentation(numBlocksPtr, totalBytesWastedPtr, fragBytesWastedPtr)
  3492.     int    *numBlocksPtr;        /* Return number of blocks in the cache. */
  3493.     int    *totalBytesWastedPtr;    /* Return the total number of bytes wasted in
  3494.                  * the cache. */
  3495.     int    *fragBytesWastedPtr;    /* Return the number of bytes wasted when cache
  3496.                  * is caches 1024 byte fragments. */
  3497. {
  3498.     register Fscache_Block     *blockPtr;
  3499.     register List_Links          *listPtr, *lPtr;
  3500.     register int        numBlocks = 0;
  3501.     register int        totalBytesWasted = 0;
  3502.     register int        fragBytesWasted = 0;
  3503.     register int        bytesInBlock;
  3504.     int                numFrags;
  3505.  
  3506.     LOCK_MONITOR;
  3507.  
  3508.     listPtr = lruList;
  3509.     LIST_FORALL(listPtr, lPtr) {
  3510.     blockPtr = USE_LINKS_TO_BLOCK(lPtr);
  3511.     if ((blockPtr->refCount > 0) || (blockPtr->blockSize < 0)) {
  3512.         /* 
  3513.          * Skip locked blocks because they might be in the process of
  3514.          * being modified.
  3515.          */
  3516.         continue;
  3517.     }
  3518.     numBlocks++;
  3519.     bytesInBlock = blockPtr->blockSize;
  3520.     if (bytesInBlock < FS_BLOCK_SIZE) {
  3521.         totalBytesWasted += FS_BLOCK_SIZE - bytesInBlock;
  3522.         if (blockPtr->blockNum < FSDM_NUM_DIRECT_BLOCKS) {
  3523.         numFrags = (bytesInBlock - 1) / FS_FRAGMENT_SIZE + 1; 
  3524.         fragBytesWasted += FS_BLOCK_SIZE - numFrags * FS_FRAGMENT_SIZE;
  3525.         }
  3526.     }
  3527.     }
  3528.  
  3529.     *numBlocksPtr = numBlocks;
  3530.     *totalBytesWastedPtr = totalBytesWasted;
  3531.     *fragBytesWastedPtr = fragBytesWasted;
  3532.  
  3533.     UNLOCK_MONITOR;
  3534. }
  3535.  
  3536.  
  3537. /*
  3538.  * ----------------------------------------------------------------------------
  3539.  *
  3540.  * Fscache_CountBlocks --
  3541.  *
  3542.  *    Count the number of clean and dirty blocks under the specified 
  3543.  *    domain.
  3544.  *
  3545.  * Results:
  3546.  *    
  3547.  *
  3548.  * Side effects:
  3549.  *
  3550.  * ----------------------------------------------------------------------------
  3551.  *
  3552.  */
  3553. ENTRY void
  3554. Fscache_CountBlocks(serverID, majorNumber, numBlocksPtr, numDirtyBlocksPtr)
  3555.     int        serverID; /* ServerID of file. */
  3556.     int        majorNumber;  /* Major number of domain */
  3557.     int        *numBlocksPtr; /* OUT: Number of blocks in the cache. */
  3558.     int        *numDirtyBlocksPtr;  /* OUT: Number of dirty blocks in cache.*/
  3559.  
  3560. {
  3561.     register    Fscache_Block    *blockPtr;
  3562.     register    List_Links    *listPtr;
  3563.  
  3564.     LOCK_MONITOR;
  3565.  
  3566.     *numBlocksPtr = *numDirtyBlocksPtr = 0;
  3567.     LIST_FORALL(lruList, listPtr) {
  3568.     blockPtr = USE_LINKS_TO_BLOCK(listPtr);
  3569.     if ((blockPtr->cacheInfoPtr->hdrPtr->fileID.major != majorNumber) ||
  3570.         (blockPtr->cacheInfoPtr->hdrPtr->fileID.serverID != serverID)) {
  3571.         continue;
  3572.     }
  3573.     (*numBlocksPtr)++;
  3574.     if (blockPtr->flags & 
  3575.             (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  3576.         (*numDirtyBlocksPtr)++;
  3577.     } 
  3578.     }
  3579.  
  3580.     UNLOCK_MONITOR;
  3581. }
  3582.  
  3583.  
  3584. /*
  3585.  *----------------------------------------------------------------------
  3586.  *
  3587.  * Fscache_ZeroStats --
  3588.  *
  3589.  *    Zero out the FS cache counters, leaving the state variables alone.
  3590.  *
  3591.  * Results:
  3592.  *    None.
  3593.  *
  3594.  * Side effects:
  3595.  *    None.
  3596.  *
  3597.  *----------------------------------------------------------------------
  3598.  */
  3599.  
  3600. ENTRY void
  3601. Fscache_ZeroStats()
  3602. {
  3603.     unsigned int minCacheBlocks; /* state variables to preserve */
  3604.     unsigned int maxCacheBlocks;
  3605.     unsigned int maxNumBlocks;
  3606.     unsigned int numCacheBlocks;
  3607.     unsigned int numFreeBlocks;
  3608.  
  3609.     LOCK_MONITOR;
  3610.     minCacheBlocks = fs_Stats.blockCache.minCacheBlocks;
  3611.     maxCacheBlocks = fs_Stats.blockCache.maxCacheBlocks;
  3612.     maxNumBlocks = fs_Stats.blockCache.maxNumBlocks;
  3613.     numCacheBlocks = fs_Stats.blockCache.numCacheBlocks;
  3614.     numFreeBlocks = fs_Stats.blockCache.numFreeBlocks;
  3615.  
  3616.     bzero(&fs_Stats.blockCache, sizeof(fs_Stats.blockCache));
  3617.  
  3618.     fs_Stats.blockCache.minCacheBlocks = minCacheBlocks;
  3619.     fs_Stats.blockCache.maxCacheBlocks = maxCacheBlocks;
  3620.     fs_Stats.blockCache.maxNumBlocks = maxNumBlocks;
  3621.     fs_Stats.blockCache.numCacheBlocks = numCacheBlocks;
  3622.     fs_Stats.blockCache.numFreeBlocks = numFreeBlocks;
  3623.  
  3624.     UNLOCK_MONITOR;
  3625. }
  3626.